1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.vectomatic.file.client;
19
20 import java.util.ArrayList;
21 import java.util.List;
22
23 import org.vectomatic.dnd.DataTransferExt;
24 import org.vectomatic.dnd.DropPanel;
25 import org.vectomatic.dom.svg.OMSVGRect;
26 import org.vectomatic.dom.svg.OMSVGSVGElement;
27 import org.vectomatic.dom.svg.ui.SVGImage;
28 import org.vectomatic.dom.svg.utils.OMSVGParser;
29 import org.vectomatic.dom.svg.utils.SVGConstants;
30 import org.vectomatic.file.Blob;
31 import org.vectomatic.file.ErrorCode;
32 import org.vectomatic.file.File;
33 import org.vectomatic.file.FileError;
34 import org.vectomatic.file.FileList;
35 import org.vectomatic.file.FileReader;
36 import org.vectomatic.file.FileUploadExt;
37 import org.vectomatic.file.FileUtils;
38 import org.vectomatic.file.events.ErrorEvent;
39 import org.vectomatic.file.events.ErrorHandler;
40 import org.vectomatic.file.events.LoadEndEvent;
41 import org.vectomatic.file.events.LoadEndHandler;
42
43 import com.google.gwt.core.client.EntryPoint;
44 import com.google.gwt.core.client.GWT;
45 import com.google.gwt.core.client.JsDate;
46 import com.google.gwt.dom.client.Document;
47 import com.google.gwt.dom.client.Element;
48 import com.google.gwt.dom.client.Style.Unit;
49 import com.google.gwt.event.dom.client.ChangeEvent;
50 import com.google.gwt.event.dom.client.ClickEvent;
51 import com.google.gwt.event.dom.client.DragEnterEvent;
52 import com.google.gwt.event.dom.client.DragLeaveEvent;
53 import com.google.gwt.event.dom.client.DragOverEvent;
54 import com.google.gwt.event.dom.client.DropEvent;
55 import com.google.gwt.event.dom.client.LoadEvent;
56 import com.google.gwt.event.dom.client.LoadHandler;
57 import com.google.gwt.resources.client.ClientBundle;
58 import com.google.gwt.resources.client.CssResource;
59 import com.google.gwt.typedarrays.client.Int8ArrayNative;
60 import com.google.gwt.typedarrays.shared.ArrayBuffer;
61 import com.google.gwt.typedarrays.shared.Int8Array;
62 import com.google.gwt.uibinder.client.UiBinder;
63 import com.google.gwt.uibinder.client.UiField;
64 import com.google.gwt.uibinder.client.UiHandler;
65 import com.google.gwt.user.client.Window;
66 import com.google.gwt.user.client.ui.Button;
67 import com.google.gwt.user.client.ui.FlowPanel;
68 import com.google.gwt.user.client.ui.Image;
69 import com.google.gwt.user.client.ui.Label;
70 import com.google.gwt.user.client.ui.RootLayoutPanel;
71 import com.google.gwt.user.client.ui.SimplePanel;
72 import com.google.gwt.user.client.ui.Widget;
73
74 public class TestAppMain implements EntryPoint {
75 private static final char[] BASE64_CHARS = {
76 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
77 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
78 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
79 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
80 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
81 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
82 '8', '9', '+', '/'
83 };
84 private static final char BASE64_PADDING = '=';
85 @UiField
86 Button resetBtn;
87 @UiField
88 Button browseBtn;
89 @UiField
90 DropPanel dropPanel;
91 @UiField
92 FileUploadExt fileUpload;
93 @UiField
94 FileUploadExt customUpload;
95 @UiField
96 FlowPanel imagePanel;
97 @UiField(provided=true)
98 static TestAppMainBundle bundle = GWT.create(TestAppMainBundle.class);
99
100 protected boolean useTypedArrays;
101 protected FileReader reader;
102 protected List<File> readQueue;
103
104 interface TestAppMainBinder extends UiBinder<FlowPanel, TestAppMain> {
105 }
106 private static TestAppMainBinder binder = GWT.create(TestAppMainBinder.class);
107 interface TestAppMainCss extends CssResource {
108 public String imagePanel();
109 public String customUpload();
110 public String dropPanel();
111 public String thumbnail();
112 @ClassName("thumbnail-image")
113 public String thumbnailImage();
114 @ClassName("thumbnail-text")
115 public String thumbnailText();
116 @ClassName("txt")
117 public String text();
118 }
119 interface TestAppMainBundle extends ClientBundle {
120 @Source("TestAppMainCss.css")
121 public TestAppMainCss css();
122 }
123
124 @Override
125 public void onModuleLoad() {
126
127 useTypedArrays = !"false".equals(Window.Location.getParameter("typedArrays"));
128
129
130 bundle.css().ensureInjected();
131 FlowPanel flowPanel = binder.createAndBindUi(this);
132 Document document = Document.get();
133 dropPanel.getElement().appendChild(document.createDivElement()).appendChild(document.createTextNode("Drop files here"));
134 RootLayoutPanel.get().add(flowPanel);
135
136
137
138 reader = new FileReader();
139 reader.addLoadEndHandler(new LoadEndHandler() {
140
141
142
143
144
145 @Override
146 public void onLoadEnd(LoadEndEvent event) {
147 if (reader.getError() == null) {
148 if (readQueue.size() > 0) {
149 File file = readQueue.get(0);
150 try {
151 imagePanel.add(createThumbnail(file));
152 } finally {
153 readQueue.remove(0);
154 readNextFile();
155 }
156 }
157 }
158 }
159 });
160
161 reader.addErrorHandler(new ErrorHandler() {
162
163
164
165
166
167 @Override
168 public void onError(ErrorEvent event) {
169 if (readQueue.size() > 0) {
170 File file = readQueue.get(0);
171 handleError(file);
172 readQueue.remove(0);
173 readNextFile();
174 }
175 }
176 });
177 readQueue = new ArrayList<File>();
178 }
179
180 private void handleError(File file) {
181 FileError error = reader.getError();
182 String errorDesc = "";
183 if (error != null) {
184 ErrorCode errorCode = error.getCode();
185 if (errorCode != null) {
186 errorDesc = ": " + errorCode.name();
187 }
188 }
189 Window.alert("File loading error for file: " + file.getName() + "\n" + errorDesc);
190 }
191
192 private void setBorderColor(String color) {
193 dropPanel.getElement().getStyle().setBorderColor(color);
194 }
195
196
197
198
199
200
201 private void processFiles(FileList files) {
202 GWT.log("length=" + files.getLength());
203 for (File file : files) {
204 readQueue.add(file);
205 }
206
207 readNextFile();
208 }
209
210
211
212
213
214
215 private void readNextFile() {
216 if (readQueue.size() > 0) {
217 File file = readQueue.get(0);
218 String type = file.getType();
219 try {
220 if ("image/svg+xml".equals(type)) {
221 reader.readAsText(file);
222 } else if (type.startsWith("image/png")) {
223
224
225
226 imagePanel.add(createThumbnail(file));
227 readQueue.remove(0);
228 readNextFile();
229 } else if (type.startsWith("image/")) {
230
231
232 if (useTypedArrays) {
233 reader.readAsArrayBuffer(file);
234 } else {
235 reader.readAsBinaryString(file);
236 }
237 } else if (type.startsWith("text/")) {
238
239
240 Blob blob = file;
241 if (file.getSize() > 0) {
242 blob = file.slice(0, 1000, "text/plain; charset=utf-8");
243 }
244 reader.readAsText(blob);
245 }
246 } catch(Throwable t) {
247
248
249 handleError(file);
250 readQueue.remove(0);
251 readNextFile();
252 }
253 }
254 }
255
256 private FlowPanel createThumbnail(File file) {
257 FlowPanel thumbnail = new FlowPanel();
258 thumbnail.setStyleName(bundle.css().thumbnail());
259 String type = file.getType();
260 final String name = file.getName();
261 final JsDate date = file.getLastModifiedDate();
262
263 Widget image = null;
264 if ("image/svg+xml".equals(type)) {
265 image = createSvgImage();
266 } else if (type.startsWith("image/png")) {
267 image = createPngImage(file);
268 } else if (type.startsWith("image/")) {
269 image = createBitmapImage(file);
270 } else if (type.startsWith("text/")) {
271 image = createText(file);
272 }
273 SimplePanel thumbnailImage = new SimplePanel(image);
274 thumbnailImage.setStyleName(bundle.css().thumbnailImage());
275 thumbnail.add(thumbnailImage);
276
277 StringBuilder description = new StringBuilder(name);
278 if (date != null) {
279 description.append(" (");
280 description.append(date.toLocaleDateString());
281 description.append(")");
282 }
283 Label thumbnailText = new Label(description.toString());
284 thumbnailText.setStyleName(bundle.css().thumbnailText());
285 thumbnail.add(thumbnailText);
286 return thumbnail;
287 }
288
289 private SVGImage createSvgImage() {
290 GWT.log(reader.getStringResult());
291 final OMSVGSVGElement svg = OMSVGParser.parse(reader.getStringResult());
292 return new SVGImage(svg) {
293 protected void onAttach() {
294 OMSVGRect viewBox = svg.getViewBox().getBaseVal();
295 if (viewBox.getWidth() == 0 || viewBox.getHeight() == 0) {
296 OMSVGRect bbox = svg.getBBox();
297 bbox.assignTo(viewBox);
298 }
299 svg.getStyle().setWidth(150, Unit.PX);
300 svg.getStyle().setHeight(150, Unit.PX);
301 super.onAttach();
302 }
303 };
304 }
305
306 private Image createPngImage(final File file) {
307 final Image image = new Image();
308 final String url = FileUtils.createObjectURL(file);
309 image.addLoadHandler(new LoadHandler() {
310 @Override
311 public void onLoad(LoadEvent event) {
312 sizeBitmap(image);
313 FileUtils.revokeObjectURL(url);
314 }
315 });
316 image.setUrl(url);
317 return image;
318 }
319
320 private Image createBitmapImage(final File file) {
321 String url;
322 if (useTypedArrays) {
323 ArrayBuffer buffer = reader.getArrayBufferResult();
324 Int8Array array = Int8ArrayNative.create(buffer);
325 url = "data:" + file.getType() + ";base64," + toBase64(array);
326 } else {
327 String result = reader.getStringResult();
328 url = FileUtils.createDataUrl(file.getType(), result);
329 }
330 final Image image = new Image();
331 image.setVisible(false);
332 image.addLoadHandler(new LoadHandler() {
333 @Override
334 public void onLoad(LoadEvent event) {
335 sizeBitmap(image);
336 }
337 });
338 image.setUrl(url);
339 return image;
340 }
341
342 private void sizeBitmap(Image image) {
343 int width = image.getWidth();
344 if (width == 0) {
345 width = ieWidth(image.getElement());
346 }
347 int height = image.getHeight();
348 if (height == 0) {
349 height = ieHeight(image.getElement());
350 }
351 GWT.log("size=" + width + "x" + height);
352 float f = 150.0f / Math.max(width, height);
353 int w = (int)(f * width);
354 int h = (int)(f * height);
355 image.setPixelSize(w, h);
356 image.getElement().getStyle().setWidth(w, Unit.PX);
357 image.getElement().getStyle().setHeight(h, Unit.PX);
358 image.setVisible(true);
359 }
360
361 private FlowPanel createText(final File file) {
362 String result = reader.getStringResult();
363 FlowPanel panel = new FlowPanel();
364 panel.getElement().appendChild(Document.get().createTextNode(result));
365 panel.addStyleName(bundle.css().text());
366 return panel;
367 }
368
369 @UiHandler("browseBtn")
370 public void browse(ClickEvent event) {
371 customUpload.click();
372 }
373
374 @UiHandler("resetBtn")
375 public void reset(ClickEvent event) {
376 for (int i = imagePanel.getWidgetCount() - 1; i >= 0; i--) {
377 imagePanel.remove(i);
378 }
379 }
380
381 @UiHandler("fileUpload")
382 public void uploadFile1(ChangeEvent event) {
383 processFiles(fileUpload.getFiles());
384 }
385
386 @UiHandler("customUpload")
387 public void uploadFile2(ChangeEvent event) {
388 processFiles(customUpload.getFiles());
389 }
390
391 @UiHandler("dropPanel")
392 public void onDragOver(DragOverEvent event) {
393
394
395
396 event.stopPropagation();
397 event.preventDefault();
398 }
399
400 @UiHandler("dropPanel")
401 public void onDragEnter(DragEnterEvent event) {
402 setBorderColor(SVGConstants.CSS_RED_VALUE);
403 event.stopPropagation();
404 event.preventDefault();
405 }
406
407 @UiHandler("dropPanel")
408 public void onDragLeave(DragLeaveEvent event) {
409 setBorderColor(SVGConstants.CSS_BLACK_VALUE);
410 event.stopPropagation();
411 event.preventDefault();
412 }
413
414 @UiHandler("dropPanel")
415 public void onDrop(DropEvent event) {
416 processFiles(event.getDataTransfer().<DataTransferExt>cast().getFiles());
417 setBorderColor(SVGConstants.CSS_BLACK_VALUE);
418 event.stopPropagation();
419 event.preventDefault();
420 }
421
422 public static String toBase64(Int8Array array) {
423
424
425 StringBuilder builder = new StringBuilder();
426 int length = array.length();
427 if (length > 0) {
428 char[] charArray = new char[4];
429 int ix = 0;
430 while (length >= 3) {
431 int i = ((array.get(ix) & 0xff)<<16)
432 + ((array.get(ix+1) & 0xff)<<8)
433 + (array.get(ix+2) & 0xff);
434 charArray[0] = BASE64_CHARS[i>>18];
435 charArray[1] = BASE64_CHARS[(i>>12) & 0x3f];
436 charArray[2] = BASE64_CHARS[(i>>6) & 0x3f];
437 charArray[3] = BASE64_CHARS[i & 0x3f];
438 builder.append(charArray);
439 ix += 3;
440 length -= 3;
441 }
442 if (length == 1) {
443 int i = array.get(ix)&0xff;
444 charArray[0] = BASE64_CHARS[i>>2];
445 charArray[1] = BASE64_CHARS[(i<<4)&0x3f];
446 charArray[2] = BASE64_PADDING;
447 charArray[3] = BASE64_PADDING;
448 builder.append(charArray);
449 } else if (length == 2) {
450 int i = ((array.get(ix) & 0xff)<<8)
451 + (array.get(ix+1) & 0xff);
452 charArray[0] = BASE64_CHARS[i>>10];
453 charArray[1] = BASE64_CHARS[(i>>4) & 0x3f];
454 charArray[2] = BASE64_CHARS[(i<<2) & 0x3f];
455 charArray[3] = BASE64_PADDING;
456 builder.append(charArray);
457 }
458 }
459 return builder.toString();
460 }
461
462
463 private static native int ieWidth(Element elt)
464
465 ;
466 private static native int ieHeight(Element elt)
467
468 ;
469 }