Test Failed
Push — rendered-cell-sizes ( 8ba9d2 )
by Stefan
03:31
created

SizeCalculator::getCharacterWidth()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 3
1
<?php
2
3
namespace OneSheet\Size;
4
5
/**
6
 * Class SizeCalculator to calculate the approximate width
7
 * of cells based on the current font, size and content.
8
 *
9
 * @package OneSheet\Size
10
 */
11
class SizeCalculator
12
{
13
    /**
14
     * @var array
15
     */
16
    private $fonts;
17
18
    /**
19
     * @var array
20
     */
21
    private $sizes;
22
23
    /**
24
     * @param string|null $fontsDirectory
25
     */
26
    public function __construct($fontsDirectory)
27
    {
28
        $fontsPattern = $this->determineFontsPattern($fontsDirectory);
29
        foreach (glob($fontsPattern) as $path) {
30
            $meta = new FontMeta($path);
31
            if (strlen($meta->getFontName())) {
32
                $this->fonts[$meta->getFontName()] = $path;
33
            }
34
        }
35
    }
36
37
    /**
38
     * Get the calculated cell width for a given value.
39
     *
40
     * @param string $fontName
41
     * @param int    $fontSize
42
     * @param mixed  $value
43
     *
44
     * @return float|int
45
     */
46
    public function getCellWidth($fontName, $fontSize, $value)
47
    {
48
        $width = 1;
49
        foreach ($this->getSingleCharacterArray($value) as $char) {
50
            $width += $this->getCharacterWidth($fontName, $fontSize, $char);
51
        }
52
53
        return $width;
54
    }
55
56
    /**
57
     * Get width of a single character. Calculate & cache if necessary.
58
     *
59
     * @param string $fontName
60
     * @param int    $fontSize
61
     * @param string $char
62
     *
63
     * @return float
64
     */
65
    private function getCharacterWidth($fontName, $fontSize, $char)
66
    {
67
        if (!isset($this->sizes[$fontName][$fontSize][$char])) {
68
            $this->sizes[$fontName][$fontSize][$char] =
69
                $this->calculateCharacterWith($fontName, $fontSize, $char);
70
        }
71
72
        return $this->sizes[$fontName][$fontSize][$char];
73
    }
74
75
    /**
76
     * Calculate the width of a single character for the given fontname and size.
77
     * Create image that contains the character 100 times to get more accurate results.
78
     *
79
     * @param string $fontName
80
     * @param int    $fontSize
81
     * @param string $char
82
     *
83
     * @return float
84
     */
85
    private function calculateCharacterWith($fontName, $fontSize, $char)
86
    {
87
        if (isset($this->fonts[$fontName])) {
88
            $box = imageftbbox($fontSize, 0, $this->fonts[$fontName], str_repeat($char, 100));
89
            $width = abs($box[4] - $box[0]) / 6.73 / 100;
90
            return round($width, 3);
91
        }
92
93
        return 0.1 * $fontSize;
94
    }
95
96
    /**
97
     * Split value into individual characters.
98
     *
99
     * @param mixed $value
100
     * @return array
101
     */
102
    private function getSingleCharacterArray($value)
103
    {
104
        if (mb_strlen($value) == strlen($value)) {
105
            return str_split($value);
106
        }
107
108
        return preg_split('~~u', $value, -1, PREG_SPLIT_NO_EMPTY);
109
    }
110
111
    /**
112
     * Determine glob pattern by fonts directory.
113
     *
114
     * @param $fontsDirectory
115
     * @return string
116
     */
117
    private function determineFontsPattern($fontsDirectory)
118
    {
119
        if (!isset($fontsDirectory) || !is_dir($fontsDirectory)) {
120
            $fontsDirectory = '/usr/share/fonts';
121
            if (false !== stripos(php_uname(), 'win')) {
122
                $fontsDirectory = 'C:/Windows/Fonts';
123
            }
124
        }
125
126
        return rtrim($fontsDirectory, DIRECTORY_SEPARATOR) . '/*.ttf';
127
    }
128
}
129