-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathImageCrop.java
More file actions
392 lines (349 loc) · 10.5 KB
/
ImageCrop.java
File metadata and controls
392 lines (349 loc) · 10.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
/*-
* #%L
* Image Crop Add-on
* %%
* Copyright (C) 2024-2025 Flowing Code
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package com.flowingcode.vaadin.addons.imagecrop;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import org.apache.commons.lang3.StringUtils;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.Tag;
import com.vaadin.flow.component.dependency.CssImport;
import com.vaadin.flow.component.dependency.JsModule;
import com.vaadin.flow.component.dependency.NpmPackage;
import com.vaadin.flow.component.html.Image;
import com.vaadin.flow.component.react.ReactAdapterComponent;
import com.vaadin.flow.shared.Registration;
/**
* Component for cropping images based on
* <a href="https://www.npmjs.com/package/react-image-crop">react-image-crop</a>
* library.
* This component allows users to define and manipulate crop areas on images.
*
* @author Paola De Bartolo / Flowing Code
*/
@NpmPackage(value = "react-image-crop", version = "11.0.6")
@JsModule("./src/image-crop.tsx")
@Tag("image-crop")
@CssImport("react-image-crop/dist/ReactCrop.css")
@CssImport("./styles/image-crop-styles.css")
public class ImageCrop extends ReactAdapterComponent {
private static final String IMG_FULL_HEIGHT_CLASS_NAME = "img-full-height";
private String croppedImageDataUri;
/**
* Constructs an ImageCrop component with the given image URL.
*
* @param src the URL of the image to be cropped
*/
public ImageCrop(String src) {
this.setImageSrc(src);
this.addCroppedImageListener(this::updateCroppedImage);
this.croppedImageDataUri = src;
}
/**
* Constructs an ImageCrop component with the given image.
*
* @param image the image to be cropped
* @deprecated This constructor only preserves the image URL and {@linkplain #setImageAlt(String)
* alternate text}. Use {@link #ImageCrop(String)} instead.
*/
@Deprecated(forRemoval = true, since = "1.2.0")
public ImageCrop(Image image) {
this(image.getSrc());
image.getAlt().ifPresent(a -> this.setImageAlt(a));
}
/**
* Adds a listener for the {@link CroppedImageEvent} fired when the
* cropped image is updated.
*
* @param listener the listener to be added
* @return a registration for the listener, which can be used to remove the
* listener
*/
protected Registration addCroppedImageListener(
ComponentEventListener<CroppedImageEvent> listener) {
return this.addListener(CroppedImageEvent.class, listener);
}
/**
* Updates the cropped image data URI based on the event data.
*
* @param event the event containing the new cropped image data URI
*/
private void updateCroppedImage(CroppedImageEvent event) {
this.croppedImageDataUri = event.getCroppedImageDataUri();
}
/**
* Sets the source of the image to be cropped.
*
* @param imageSrc the image source
*/
public void setImageSrc(String imageSrc) {
setState("imgSrc", imageSrc);
}
/**
* Gets the source of the image being cropped.
*
* @return the image source
*/
public String getImageSrc() {
return getState("imgSrc", String.class);
}
/**
* Sets the alternative information of the image to be cropped.
*
* @param imageAlt the image alternative information
*/
public void setImageAlt(String imageAlt) {
setState("imgAlt", imageAlt);
}
/**
* Gets the alternative information of the image being cropped.
*
* @return the image alternative information
*/
public String getImageAlt() {
return getState("imgAlt", String.class);
}
/**
* Defines the crop dimensions.
*
* @param crop the crop dimensions
*/
public void setCrop(Crop crop) {
setState("crop", crop);
getElement().executeJs("this._updateCroppedImage(this.crop)");
}
/**
* Gets the crop dimensions.
*/
public Crop getCrop() {
return getState("crop", Crop.class);
}
/**
* Sets the aspect ratio of the crop.
* For example, 1 for a square or 16/9 for landscape.
*
* @param aspect the aspect ratio of the crop
*/
public void setAspect(double aspect) {
setState("aspect", aspect);
}
/**
* Gets the aspect ratio of the crop.
*
* @return the aspect ratio
*/
public double getAspect() {
return getState("aspect", Double.class);
}
/**
* Sets whether the crop area should be shown as a circle.
* If the aspect ratio is not 1, the circle will be warped into an oval shape.
* Defaults to false.
*
* @param circularCrop true to show the crop area as a circle, false otherwise
*/
public void setCircularCrop(boolean circularCrop) {
setState("circularCrop", circularCrop);
}
/**
* Gets whether the crop area is shown as a circle.
*
* @return true if the crop area is a circle, false otherwise
*/
public boolean isCircularCrop() {
return getState("circularCrop", Boolean.class);
}
/**
* Sets whether the selection can't be disabled if the user clicks outside
* the selection area. Defaults to false.
*
* @param keepSelection true so selection can't be disabled if the user clicks
* outside the selection area, false otherwise.
*/
public void setKeepSelection(boolean keepSelection) {
setState("keepSelection", keepSelection);
}
/**
* Gets whether the selection is enabled.
*
* @return true if the selection is enabled, false otherwise
*/
public boolean isKeepSelection() {
return getState("keepSelection", Boolean.class);
}
/**
* Sets whether the user cannot resize or draw a new crop. Defaults to false.
*
* @param disabled true to disable crop resizing and drawing, false otherwise
*/
public void setDisabled(boolean disabled) {
setState("disabled", disabled);
}
/**
* Gets whether the crop resizing and drawing is disabled.
*
* @return true if disabled, false otherwise
*/
public boolean isDisabled() {
return getState("disabled", Boolean.class);
}
/**
* Sets whether the user cannot create or resize a crop, but can still drag the
* existing crop around. Defaults to false.
*
* @param locked true to lock the crop, false otherwise
*/
public void setLocked(boolean locked) {
setState("locked", locked);
}
/**
* Gets whether the crop is locked.
*
* @return true if the crop is locked, false otherwise
*/
public boolean isLocked() {
return getState("locked", Boolean.class);
}
/**
* Sets a minimum crop width, in pixels.
*
* @param minWidth the minimum crop width
*/
public void setCropMinWidth(Integer minWidth) {
setState("minWidth", minWidth);
}
/**
* Gets the minimum crop width, in pixels.
*
* @return the minimum crop width
*/
public Integer getCropMinWidth() {
return getState("minWidth", Integer.class);
}
/**
* Sets a minimum crop height, in pixels.
*
* @param minHeight the minimum crop height
*/
public void setCropMinHeight(Integer minHeight) {
setState("minHeight", minHeight);
}
/**
* Gets the minimum crop height, in pixels.
*
* @return the minimum crop height
*/
public Integer getCropMinHeight() {
return getState("minHeight", Integer.class);
}
/**
* Sets a maximum crop width, in pixels.
*
* @param maxWidth the maximum crop width
*/
public void setCropMaxWidth(Integer maxWidth) {
setState("maxWidth", maxWidth);
}
/**
* Gets the maximum crop width, in pixels.
*
* @return the maximum crop width
*/
public Integer getCropMaxWidth() {
return getState("maxWidth", Integer.class);
}
/**
* Sets a maximum crop height, in pixels.
*
* @param maxHeight the maximum crop height
*/
public void setCropMaxHeight(Integer maxHeight) {
setState("maxHeight", maxHeight);
}
/**
* Gets the maximum crop height, in pixels.
*
* @return the maximum crop height
*/
public Integer getCropMaxHeight() {
return getState("maxHeight", Integer.class);
}
/**
* Sets whether to show rule of thirds lines in the cropped area. Defaults to
* false.
*
* @param ruleOfThirds true to show rule of thirds lines, false otherwise
*/
public void setRuleOfThirds(boolean ruleOfThirds) {
setState("ruleOfThirds", ruleOfThirds);
}
/**
* Gets whether rule of thirds lines are shown in the cropped area.
*
* @return true if rule of thirds lines are shown, false otherwise
*/
public boolean isRuleOfThirds() {
return getState("ruleOfThirds", Boolean.class);
}
/**
* Returns the cropped image data URI.
*
* @return the cropped image data URI
*/
public String getCroppedImageDataUri() {
return this.croppedImageDataUri;
}
/**
* Sets the image to occupy the full viewport height when enabled.
* If {@code fullHeight} is {@code true}, applies a CSS class that
* sets the image height to 100vh. If {@code false}, removes the class
* to revert to the default height.
*
* @param fullHeight whether the image should fill the viewport height
*/
public void setImageFullHeight(Boolean fullHeight) {
if (fullHeight)
this.addClassName(IMG_FULL_HEIGHT_CLASS_NAME);
else
this.removeClassName(IMG_FULL_HEIGHT_CLASS_NAME);
}
/**
* Decodes the cropped image data URI and returns it as a byte array. If the image data URI is not
* in the format "data:image/*;base64,", it will be decoded assuming it is a Base64 encoded
* string.
*
* <p>
* This method incorporates work licensed under MIT. Copyright 2021-2023 David "F0rce" Dodlek
* https://github.com/F0rce/cropper
* </p>
*
* @return byte[] the decoded byte array of the cropped image
*/
public byte[] getCroppedImageBase64() {
String croppedDataUri = this.getCroppedImageDataUri();
if (StringUtils.isBlank(croppedDataUri)) {
return null;
}
String base64Data = croppedDataUri;
if (croppedDataUri.contains("base64,")) {
base64Data = croppedDataUri.split(",")[1];
}
return Base64.getDecoder().decode(base64Data.getBytes(StandardCharsets.UTF_8));
}
}