Passed
Push — complex-text-wrapping ( f40da2 )
by Burhan
02:32
created

FieldRendererComplexText::wrap()   A

Complexity

Conditions 6
Paths 9

Size

Total Lines 19
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 11
c 1
b 0
f 0
nc 9
nop 4
dl 0
loc 19
ccs 0
cts 11
cp 0
crap 42
rs 9.2222
1
<?php
2
3
namespace Graze\CiffRenderer\Renderer\FieldRenderer;
4
5
use Graze\CiffRenderer\Parser\FieldParser\FieldParserComplexText;
6
use Graze\CiffRenderer\Parser\FieldParser\FieldParserInterface;
7
use Intervention\Image\Gd\Font;
8
use Intervention\Image\ImageManager;
9
10
class FieldRendererComplexText implements FieldRendererInterface
11
{
12
    /**
13
     * @var float
14
     */
15
    const LETTER_SPACING_ADJUSTMENT = 0.97;
16
17
    /**
18
     * @var Font
19
     */
20
    private $font;
21
22
    /**
23
     * @param Font $font
24
     */
25 1
    public function __construct(Font $font)
26
    {
27 1
        $this->font = $font;
28 1
    }
29
30
    /**
31
     * @param ImageManager $imageManager
32
     * @param FieldParserInterface $parser
33
     * @param null|callable $fontResolver
34
     * @param null|callable $graphicResolver
35
     * @return \Intervention\Image\Image
36
     */
37
    public function render(
38
        ImageManager $imageManager,
39
        FieldParserInterface $parser,
40
        callable $fontResolver = null,
41
        callable $graphicResolver = null
42
    ) {
43
        $fontPath = $fontResolver($parser->getFontFace());
44
45
        $lines = $parser->getText();
46
        $texts = [];
47
        $height = 0;
48
        $text = $this->renderLine($imageManager, $parser, $fontPath, $lines);
49
50
        // store dimensions ready to create a canvas
51
        $width = $text->getWidth();
52
        $height += $text->getHeight();
53
54
        $texts[] = $text;
55
56
        if (empty($texts)) {
57
            // nothing to render
58
            return null;
59
        }
60
61
        // create a canvas to add each line of text to
62
        $canvas = $imageManager->canvas($width, $height);
63
64
        $offsetX = 0;
65
        $offsetY = 0;
66
        foreach ($texts as $text) {
67
            // add the text to the canvas
68
            $canvas->insert($text, 'top-left', (int) $offsetX, (int) $offsetY);
69
70
            // increase the offset for the next line
71
            $offsetY += $text->getHeight();
72
        }
73
74
        $orientation = $parser->getOrientation();
75
        if ($orientation) {
76
            $canvas->rotate(360 - $orientation);
77
        }
78
79
        return $canvas;
80
    }
81
82
    /**
83
     * @param ImageManager $imageManager
84
     * @param FieldParserInterface $parser
85
     * @param string $fontPath
86
     * @param string $text
87
     * @return \Intervention\Image\Image
88
     */
89
    private function renderLine(ImageManager $imageManager, FieldParserInterface $parser, $fontPath, $text)
90
    {
91
        // Pass the text through the wrapping function first
92
        $text = $this->wrap($parser->getFontSize(), $fontPath, $text, $parser->getWidth());
93
94
        // create the text here so it can be measured
95
        $this->font->text($text);
96
97
        $this->font->file($fontPath);
98
        $this->font->size($parser->getFontSize());
99
        $this->font->color('#000');
100
        $this->font->align($parser->getTextAlignment());
101
        $this->font->valign('top');
102
103
        $size = $this->font->getBoxSize();
104
105
        // if text consists of invisible chars, measured size will be zero, nothing to print
106
        if ($size['width'] < 1 || $size['height'] < 1) {
107
            return null;
108
        }
109
110
        $fontCallback = function (&$font) {
111
            // override the font created by Canvas::text()
112
            $font = $this->font;
113
        };
114
115
        $canvas = $imageManager->canvas($size['width'], $size['height']);
116
117
        // figure out the correct reference point for this alignment
118
        switch ($parser->getTextAlignment()) {
119
            case FieldParserComplexText::ALIGN_CENTRE:
120
                $posX = round($parser->getWidth()/2);
121
                break;
122
123
            case FieldParserComplexText::ALIGN_RIGHT:
124
                $posX = $parser->getWidth();
125
                break;
126
127
            default:
128
                $posX = 0;
129
                break;
130
        }
131
132
        // no need to pass the text in here as we override the font in the callback
133
        $canvas->text('', $posX, 0, $fontCallback);
0 ignored issues
show
Bug introduced by
It seems like $posX can also be of type double; however, parameter $x of Intervention\Image\Image::text() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

133
        $canvas->text('', /** @scrutinizer ignore-type */ $posX, 0, $fontCallback);
Loading history...
134
135
        // attempt to correct differences between this and Clarisoft
136
        $width = (int) round($size['width'] * self::LETTER_SPACING_ADJUSTMENT);
137
        $canvas->resize($width, $size['height']);
138
139
        return $canvas;
140
    }
141
142
    /**
143
     * Wrap the given text such that it doesn't exceed the given width using the given font and size.
144
     * @param string $fontSize
145
     * @param string $fontFace
146
     * @param string $string
147
     * @param int $width
148
     * @return string
149
     */
150
    function wrap($fontSize, $fontFace, $string, $width){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
151
        // Font size needs to be scaled a bit to work correctly.
152
        $fontSize = $fontSize * 0.8;
153
        $wrappedString = "";
154
155
        $words = explode(' ', $string);
156
157
        foreach ($words as $word){
158
159
            $teststring = $wrappedString=="" ? $word : $wrappedString.' '.$word;
160
            $testbox = imagettfbbox($fontSize, 0, $fontFace, $teststring);
161
            if ( $testbox[2] > $width ){
162
                $wrappedString.=($wrappedString=="" ? "" : "\n").$word;
163
            } else {
164
                $wrappedString.=($wrappedString=="" ? "" : ' ').$word;
165
            }
166
        }
167
168
        return $wrappedString;
169
    }
170
171
    /**
172
     * @return FieldRendererInterface
173
     */
174 1
    public static function factory()
175
    {
176 1
        return new static(
177 1
            new Font()
178
        );
179
    }
180
}
181