7
7
use Intervention \Image \Drivers \Vips \Core ;
8
8
use Intervention \Image \Drivers \Vips \FontProcessor ;
9
9
use Intervention \Image \Exceptions \RuntimeException ;
10
+ use Intervention \Image \Geometry \Factories \CircleFactory ;
11
+ use Intervention \Image \Geometry \Rectangle ;
10
12
use Intervention \Image \Interfaces \FrameInterface ;
11
13
use Intervention \Image \Interfaces \ImageInterface ;
14
+ use Intervention \Image \Interfaces \PointInterface ;
12
15
use Intervention \Image \Interfaces \SpecializedInterface ;
13
16
use Intervention \Image \Modifiers \TextModifier as GenericTextModifier ;
17
+ use Intervention \Image \Typography \TextBlock ;
14
18
use Jcupitt \Vips \BlendMode ;
15
19
use Jcupitt \Vips \Image as VipsImage ;
16
20
use Jcupitt \Vips \Exception as VipsException ;
@@ -26,87 +30,125 @@ class TextModifier extends GenericTextModifier implements SpecializedInterface
26
30
*/
27
31
public function apply (ImageInterface $ image ): ImageInterface
28
32
{
33
+ $ textBlock = new TextBlock ($ this ->text );
29
34
$ fontProcessor = new FontProcessor ();
35
+
36
+ // decode text color
30
37
$ color = $ this ->driver ()->handleInput ($ this ->font ->color ());
31
- $ strokeColor = $ this ->driver ()->handleInput ($ this ->font ->strokeColor ());
32
- $ lines = $ fontProcessor ->textBlock ($ this ->text , $ this ->font , $ this ->position );
33
38
34
- foreach ($ lines as $ line ) {
35
- // build vips image from text
36
- $ text = $ fontProcessor ->textToVipsImage ((string ) $ line , $ this ->font , $ color );
39
+ // build vips image with text
40
+ $ textBlockImage = $ fontProcessor ->textToVipsImage ($ this ->text , $ this ->font , $ color );
41
+
42
+ // calculate block position
43
+ $ blockSize = $ this ->blockSize ($ textBlockImage );
44
+
45
+ // calculate baseline
46
+ $ capImage = $ fontProcessor ->textToVipsImage ('T ' , $ this ->font );
47
+ $ baseline = $ capImage ->height + $ capImage ->yoffset ;
48
+
49
+ // adjust block size
50
+ switch ($ this ->font ->valignment ()) {
51
+ case 'top ' :
52
+ $ blockSize ->movePointsY ($ baseline * -1 );
53
+ $ blockSize ->movePointsY ($ textBlockImage ->yoffset );
54
+ $ blockSize ->movePointsY ($ capImage ->height );
55
+ break ;
56
+
57
+ case 'bottom ' :
58
+ $ lastLineImage = $ fontProcessor ->textToVipsImage ((string ) $ textBlock ->last (), $ this ->font );
59
+ $ blockSize ->movePointsY ($ lastLineImage ->height );
60
+ $ blockSize ->movePointsY ($ baseline * -1 );
61
+ $ blockSize ->movePointsY ($ lastLineImage ->yoffset );
62
+ break ;
63
+ }
37
64
38
- // original line height from vips image before rotation
39
- $ height = $ text -> height ;
65
+ // apply rotation
66
+ $ blockSize -> rotate ( $ this -> font -> angle ()) ;
40
67
41
- // apply rotation
42
- $ text = $ this -> maybeRotateText ( $ text );
68
+ // extract block position
69
+ $ blockPosition = clone $ blockSize -> last ( );
43
70
44
- if ($ this ->font ->hasStrokeEffect ()) {
45
- // build stroke text image if applicable
46
- $ stroke = $ fontProcessor ->textToVipsImage ((string ) $ line , $ this ->font , $ strokeColor );
71
+ // apply text rotation if necessary
72
+ $ textBlockImage = $ this ->maybeRotateText ($ textBlockImage );
47
73
48
- // original line height from vips image before rotation
49
- $ strokeHeight = $ stroke ->height ;
74
+ // apply rotation offset to block position
75
+ if ($ this ->font ->angle () != 0 ) {
76
+ $ blockPosition ->move (
77
+ $ textBlockImage ->xoffset * -1 ,
78
+ $ textBlockImage ->yoffset * -1
79
+ );
80
+ }
50
81
51
- // apply rotation for stroke effect
52
- $ stroke = $ this ->maybeRotateText ($ stroke );
53
- }
82
+ if ($ this ->font ->hasStrokeEffect ()) {
83
+ // decode stroke color
84
+ $ strokeColor = $ this ->driver ()->handleInput ($ this ->font ->strokeColor ());
85
+
86
+ // build stroke text image if applicable
87
+ $ stroke = $ fontProcessor ->textToVipsImage ($ this ->text , $ this ->font , $ strokeColor );
54
88
55
- if (!$ image ->isAnimated ()) {
56
- $ modified = $ image ->core ()->first ();
89
+ // apply rotation for stroke effect
90
+ $ stroke = $ this ->maybeRotateText ($ stroke );
91
+ }
92
+
93
+ if (!$ image ->isAnimated ()) {
94
+ $ modified = $ image ->core ()->first ();
95
+
96
+ if (isset ($ stroke )) {
97
+ // draw stroke effect with offsets
98
+ foreach ($ this ->strokeOffsets ($ this ->font ) as $ offset ) {
99
+ $ modified = $ this ->placeTextOnFrame (
100
+ $ stroke ,
101
+ $ modified ,
102
+ $ blockPosition ->x () - $ offset ->x (),
103
+ $ blockPosition ->y () - $ offset ->y ()
104
+ );
105
+ }
106
+ }
57
107
58
- if (isset ($ stroke ) && isset ($ strokeHeight )) {
108
+ // place text image on original image
109
+ $ modified = $ this ->placeTextOnFrame (
110
+ $ textBlockImage ,
111
+ $ modified ,
112
+ $ blockPosition ->x (),
113
+ $ blockPosition ->y ()
114
+ );
115
+
116
+ $ modified = $ modified ->native ();
117
+ } else {
118
+ $ frames = [];
119
+ foreach ($ image as $ frame ) {
120
+ $ modifiedFrame = $ frame ;
121
+
122
+ if (isset ($ stroke )) {
59
123
// draw stroke effect with offsets
60
124
foreach ($ this ->strokeOffsets ($ this ->font ) as $ offset ) {
61
- $ modified = $ this ->placeTextOnFrame (
125
+ $ modifiedFrame = $ this ->placeTextOnFrame (
62
126
$ stroke ,
63
- $ modified ,
64
- $ line -> position () ->x () - $ offset ->x (),
65
- $ line -> position ()-> y () - $ strokeHeight - $ offset ->y (),
127
+ $ modifiedFrame ,
128
+ $ blockPosition ->x () - $ offset ->x (),
129
+ $ blockPosition -> y () - $ offset ->y ()
66
130
);
67
131
}
68
132
}
69
133
70
134
// place text image on original image
71
- $ modified = $ this ->placeTextOnFrame (
72
- $ text ,
73
- $ modified ,
74
- $ line -> position () ->x (),
75
- $ line -> position ()-> y () - $ height ,
135
+ $ modifiedFrame = $ this ->placeTextOnFrame (
136
+ $ textBlockImage ,
137
+ $ modifiedFrame ,
138
+ $ blockPosition ->x (),
139
+ $ blockPosition -> y ()
76
140
);
77
141
78
- $ modified = $ modified ->native ();
79
- } else {
80
- $ frames = [];
81
- foreach ($ image as $ frame ) {
82
- $ modifiedFrame = $ frame ;
83
- if (isset ($ stroke ) && isset ($ strokeHeight )) {
84
- // draw stroke effect with offsets
85
- foreach ($ this ->strokeOffsets ($ this ->font ) as $ offset ) {
86
- $ modifiedFrame = $ this ->placeTextOnFrame (
87
- $ stroke ,
88
- $ modifiedFrame ,
89
- $ line ->position ()->x () - $ offset ->x (),
90
- $ line ->position ()->y () - $ strokeHeight - $ offset ->y (),
91
- );
92
- }
93
- }
94
- // place text image on original image
95
- $ modifiedFrame = $ this ->placeTextOnFrame (
96
- $ text ,
97
- $ modifiedFrame ,
98
- $ line ->position ()->x (),
99
- $ line ->position ()->y () - $ height ,
100
- );
101
-
102
- $ frames [] = $ modifiedFrame ;
103
- }
104
-
105
- $ modified = Core::replaceFrames ($ image ->core ()->native (), $ frames );
142
+ $ frames [] = $ modifiedFrame ;
106
143
}
107
- $ image ->core ()->setNative ($ modified );
144
+
145
+ $ modified = Core::replaceFrames ($ image ->core ()->native (), $ frames );
108
146
}
109
147
148
+ $ image ->core ()->setNative ($ modified );
149
+
150
+ $ this ->debugPos ($ image , $ blockPosition , $ blockSize );
151
+
110
152
return $ image ;
111
153
}
112
154
@@ -128,12 +170,27 @@ private function placeTextOnFrame(VipsImage $text, FrameInterface $frame, int $x
128
170
return $ frame ;
129
171
}
130
172
173
+ /**
174
+ * Build size from given vips image
175
+ *
176
+ * @param VipsImage $blockImage
177
+ * @return Rectangle
178
+ */
179
+ private function blockSize (VipsImage $ blockImage ): Rectangle
180
+ {
181
+ $ imageSize = new Rectangle ($ blockImage ->width , $ blockImage ->height , $ this ->position );
182
+ $ imageSize ->align ($ this ->font ->alignment ());
183
+ $ imageSize ->valign ($ this ->font ->valignment ());
184
+
185
+ return $ imageSize ;
186
+ }
187
+
131
188
/**
132
189
* Maybe rotate text image according to current font angle
133
190
*
134
191
* @param VipsImage $text
135
- * @return VipsImage
136
192
* @throws VipsException
193
+ * @return VipsImage
137
194
*/
138
195
private function maybeRotateText (VipsImage $ text ): VipsImage
139
196
{
@@ -145,4 +202,32 @@ private function maybeRotateText(VipsImage $text): VipsImage
145
202
default => $ text ->similarity (['angle ' => $ this ->font ->angle ()]),
146
203
};
147
204
}
205
+
206
+ /**
207
+ * Draw debug information for given position and given rectangle size
208
+ *
209
+ * @throws RuntimeException
210
+ */
211
+ private function debugPos (ImageInterface $ image , PointInterface $ position , Rectangle $ size ): void
212
+ {
213
+ // draw pos
214
+ $ image ->drawCircle ($ position ->x (), $ position ->y (), function (CircleFactory $ circle ): void {
215
+ $ circle ->diameter (8 );
216
+ $ circle ->background ('red ' );
217
+ });
218
+
219
+ // draw points of size
220
+ foreach (array_chunk ($ size ->toArray (), 2 ) as $ point ) {
221
+ $ image ->drawCircle ($ point [0 ], $ point [1 ], function (CircleFactory $ circle ): void {
222
+ $ circle ->diameter (12 );
223
+ $ circle ->border ('green ' );
224
+ });
225
+ }
226
+
227
+ // draw size's pivot
228
+ $ image ->drawCircle ($ size ->pivot ()->x (), $ size ->pivot ()->y (), function (CircleFactory $ circle ): void {
229
+ $ circle ->diameter (20 );
230
+ $ circle ->border ('blue ' );
231
+ });
232
+ }
148
233
}
0 commit comments