1 /* 2 * Copyright (c) 2008, 2014, Oracle and/or its affiliates. 3 * All rights reserved. Use is subject to license terms. 4 * 5 * This file is available and licensed under the following license: 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * - Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * - Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the distribution. 16 * - Neither the name of Oracle Corporation nor the names of its 17 * contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 package modena; 33 34 import java.awt.Graphics; 35 import java.awt.Graphics2D; 36 import java.awt.image.BufferedImage; 37 import java.io.BufferedReader; 38 import java.io.ByteArrayInputStream; 39 import java.io.File; 40 import java.io.IOException; 41 import java.io.InputStream; 42 import java.io.InputStreamReader; 43 import java.net.MalformedURLException; 44 import java.net.URL; 45 import java.net.URLConnection; 46 import java.net.URLStreamHandler; 47 import java.net.URLStreamHandlerFactory; 48 import java.util.Locale; 49 import java.util.Map; 50 import java.util.logging.Level; 51 import java.util.logging.Logger; 52 53 import javafx.application.Application; 54 import javafx.application.Platform; 55 import javafx.embed.swing.SwingFXUtils; 56 import javafx.event.ActionEvent; 57 import javafx.event.EventHandler; 58 import javafx.fxml.FXMLLoader; 59 import javafx.geometry.NodeOrientation; 60 import javafx.geometry.Rectangle2D; 61 import javafx.scene.Node; 62 import javafx.scene.Scene; 63 import javafx.scene.SnapshotParameters; 64 import javafx.scene.control.Button; 65 import javafx.scene.control.ChoiceBox; 66 import javafx.scene.control.ColorPicker; 67 import javafx.scene.control.ComboBox; 68 import javafx.scene.control.Label; 69 import javafx.scene.control.Menu; 70 import javafx.scene.control.MenuBar; 71 import javafx.scene.control.RadioMenuItem; 72 import javafx.scene.control.ScrollPane; 73 import javafx.scene.control.Separator; 74 import javafx.scene.control.Tab; 75 import javafx.scene.control.TabPane; 76 import javafx.scene.control.ToggleButton; 77 import javafx.scene.control.ToggleGroup; 78 import javafx.scene.control.ToolBar; 79 import javafx.scene.control.Tooltip; 80 import javafx.scene.image.Image; 81 import javafx.scene.image.ImageView; 82 import javafx.scene.image.WritableImage; 83 import javafx.scene.layout.BorderPane; 84 import javafx.scene.layout.HBox; 85 import javafx.scene.layout.Pane; 86 import javafx.scene.paint.Color; 87 import javafx.scene.transform.Scale; 88 import javafx.stage.FileChooser; 89 import javafx.stage.Stage; 90 91 import javax.imageio.ImageIO; 92 93 public class Modena extends Application { 94 public static final String TEST = "test"; 95 static { 96 System.getProperties().put("javafx.pseudoClassOverrideEnabled", "true"); 97 } 98 private static final String testAppCssUrl = Modena.class.getResource("TestApp.css").toExternalForm(); 99 private static String MODENA_STYLESHEET_URL; 100 private static String MODENA_EMBEDDED_STYLESHEET_URL; 101 private static String MODENA_STYLESHEET_BASE; 102 private static String CASPIAN_STYLESHEET_URL; 103 private static String CASPIAN_STYLESHEET_BASE; 104 static { 105 try { 106 // these are not supported ways to find the platform themes and may 107 // change release to release. Just used here for testing. 108 File caspianCssFile = new File("../../../modules/controls/src/main/resources/com/sun/javafx/scene/control/skin/caspian/caspian.css"); 109 if (!caspianCssFile.exists()) { 110 caspianCssFile = new File("rt/modules/controls/src/main/resources/com/sun/javafx/scene/control/skin/caspian/caspian.css"); 111 } 112 CASPIAN_STYLESHEET_URL = caspianCssFile.exists() ? 113 caspianCssFile.toURI().toURL().toExternalForm() : 114 com.sun.javafx.scene.control.skin.ButtonSkin.class.getResource("caspian/caspian.css").toExternalForm(); 115 File modenaCssFile = new File("../../../modules/controls/src/main/resources/com/sun/javafx/scene/control/skin/modena/modena.css"); 116 if (!modenaCssFile.exists()) { 117 modenaCssFile = new File("rt/modules/controls/src/main/resources/com/sun/javafx/scene/control/skin/modena/modena.css"); 118 System.out.println("modenaCssFile = " + modenaCssFile); 119 System.out.println("modenaCssFile = " + modenaCssFile.getAbsolutePath()); 120 } 121 MODENA_STYLESHEET_URL = modenaCssFile.exists() ? 122 modenaCssFile.toURI().toURL().toExternalForm() : 123 com.sun.javafx.scene.control.skin.ButtonSkin.class.getResource("modena/modena.css").toExternalForm(); 124 MODENA_STYLESHEET_BASE = MODENA_STYLESHEET_URL.substring(0,MODENA_STYLESHEET_URL.lastIndexOf('/')+1); 125 CASPIAN_STYLESHEET_BASE = CASPIAN_STYLESHEET_URL.substring(0,CASPIAN_STYLESHEET_URL.lastIndexOf('/')+1); 126 MODENA_EMBEDDED_STYLESHEET_URL = MODENA_STYLESHEET_BASE + "modena-embedded-performance.css"; 127 System.out.println("MODENA_EMBEDDED_STYLESHEET_URL = " + MODENA_EMBEDDED_STYLESHEET_URL); 128 } catch (MalformedURLException ex) { 129 Logger.getLogger(Modena.class.getName()).log(Level.SEVERE, null, ex); 130 } 131 } 132 133 private BorderPane outerRoot; 134 private BorderPane root; 135 private SamplePageNavigation samplePageNavigation; 136 private SamplePage samplePage; 137 private Node mosaic; 138 private Node heightTest; 139 private SimpleWindowPage simpleWindows; 140 private Node combinationsTest; 141 private Node customerTest; 142 private Stage mainStage; 143 private Color backgroundColor; 144 private Color baseColor; 145 private Color accentColor; 146 private String fontName = null; 147 private int fontSize = 13; 148 private String styleSheetContent = ""; 149 private String styleSheetBase = ""; 150 private ToggleButton modenaButton,retinaButton,rtlButton,embeddedPerformanceButton; 151 private TabPane contentTabs; 152 private boolean test = false; 153 private boolean embeddedPerformanceMode = false; 154 private final EventHandler<ActionEvent> rebuild = event -> Platform.runLater(() -> { 155 updateUserAgentStyleSheet(); 156 rebuildUI(modenaButton.isSelected(), retinaButton.isSelected(), 157 contentTabs.getSelectionModel().getSelectedIndex(), 158 samplePageNavigation.getCurrentSection()); 159 }); 160 161 private static Modena instance; 162 163 public static Modena getInstance() { 164 return instance; 165 } 166 167 public Map<String, Node> getContent() { 168 return samplePage.getContent(); 169 } 170 171 public void setRetinaMode(boolean retinaMode) { 172 if (retinaMode) { 173 contentTabs.getTransforms().setAll(new Scale(2,2)); 174 } else { 175 contentTabs.getTransforms().setAll(new Scale(1,1)); 176 } 177 contentTabs.requestLayout(); 178 } 179 180 public void restart() { 181 mainStage.close(); 182 root = null; 183 accentColor = null; 184 baseColor = null; 185 backgroundColor = null; 186 fontName = null; 187 fontSize = 13; 188 try { 189 start(new Stage()); 190 } catch (Exception ex) { 191 throw new RuntimeException("Failed to start another Modena window", ex); 192 } 193 } 194 195 @Override public void start(Stage stage) throws Exception { 196 if (getParameters().getRaw().contains(TEST)) { 197 test = true; 198 } 199 mainStage = stage; 200 // set user agent stylesheet 201 updateUserAgentStyleSheet(true); 202 // build Menu Bar 203 outerRoot = new BorderPane(); 204 outerRoot.setTop(buildMenuBar()); 205 outerRoot.setCenter(root); 206 // build UI 207 rebuildUI(true,false,0, null); 208 // show UI 209 Scene scene = new Scene(outerRoot, 1024, 768); 210 scene.getStylesheets().add(testAppCssUrl); 211 stage.setScene(scene); 212 stage.setTitle("Modena"); 213 // stage.setIconified(test); // TODO: Blocked by http://javafx-jira.kenai.com/browse/JMY-203 214 stage.show(); // see SamplePage.java:110 comment on how test fails without having stage shown 215 instance = this; 216 } 217 218 private MenuBar buildMenuBar() { 219 MenuBar menuBar = new MenuBar(); 220 menuBar.setUseSystemMenuBar(true); 221 Menu fontSizeMenu = new Menu("Font"); 222 ToggleGroup tg = new ToggleGroup(); 223 fontSizeMenu.getItems().addAll( 224 buildFontRadioMenuItem("System Default", null, 0, tg), 225 buildFontRadioMenuItem("Mac (13px)", "Lucida Grande", 13, tg), 226 buildFontRadioMenuItem("Windows 100% (12px)", "Segoe UI", 12, tg), 227 buildFontRadioMenuItem("Windows 125% (15px)", "Segoe UI", 15, tg), 228 buildFontRadioMenuItem("Windows 150% (18px)", "Segoe UI", 18, tg), 229 buildFontRadioMenuItem("Linux (13px)", "Lucida Sans", 13, tg), 230 buildFontRadioMenuItem("Embedded Touch (22px)", "Arial", 22, tg), 231 buildFontRadioMenuItem("Embedded Small (9px)", "Arial", 9, tg) 232 ); 233 menuBar.getMenus().add(fontSizeMenu); 234 return menuBar; 235 } 236 237 private void updateUserAgentStyleSheet() { 238 updateUserAgentStyleSheet(modenaButton.isSelected()); 239 } 240 241 private void updateUserAgentStyleSheet(boolean modena) { 242 final SamplePage.Section scrolledSection = samplePageNavigation==null? null : samplePageNavigation.getCurrentSection(); 243 styleSheetContent = modena ? 244 loadUrl(MODENA_STYLESHEET_URL) : 245 loadUrl(CASPIAN_STYLESHEET_URL); 246 if (!modena && 247 (baseColor == null || baseColor == Color.TRANSPARENT) && 248 (backgroundColor == null || backgroundColor == Color.TRANSPARENT) && 249 (accentColor == null || accentColor == Color.TRANSPARENT) && 250 (fontName == null)) { 251 // no customizations 252 System.out.println("USING NO CUSTIMIZATIONS TO CSS, stylesheet = "+(modena?"modena":"caspian")); 253 254 // load theme 255 setUserAgentStylesheet("internal:stylesheet"+Math.random()+".css"); 256 if (root != null) root.requestLayout(); 257 // restore scrolled section 258 Platform.runLater(() -> samplePageNavigation.setCurrentSection(scrolledSection)); 259 return; 260 } 261 if (modena && embeddedPerformanceMode) styleSheetContent += loadUrl(MODENA_EMBEDDED_STYLESHEET_URL); 262 styleSheetBase = modena ? MODENA_STYLESHEET_BASE : CASPIAN_STYLESHEET_BASE; 263 styleSheetContent += "\n.root {\n"; 264 System.out.println("baseColor = "+baseColor); 265 System.out.println("accentColor = " + accentColor); 266 System.out.println("backgroundColor = " + backgroundColor); 267 if (baseColor != null && baseColor != Color.TRANSPARENT) { 268 styleSheetContent += " -fx-base:" + colorToRGBA(baseColor) + ";\n"; 269 } 270 if (backgroundColor != null && backgroundColor != Color.TRANSPARENT) { 271 styleSheetContent += " -fx-background:" + colorToRGBA(backgroundColor) + ";\n"; 272 } 273 if (accentColor != null && accentColor != Color.TRANSPARENT) { 274 styleSheetContent += " -fx-accent:" + colorToRGBA(accentColor) + ";\n"; 275 } 276 if (fontName != null) { 277 styleSheetContent += " -fx-font:"+fontSize+"px \""+fontName+"\";\n"; 278 } 279 styleSheetContent += "}\n"; 280 281 // set white background for caspian 282 if (!modena) { 283 styleSheetContent += ".needs-background {\n-fx-background-color: white;\n}"; 284 } 285 286 // load theme 287 setUserAgentStylesheet("internal:stylesheet"+Math.random()+".css"); 288 289 if (root != null) root.requestLayout(); 290 291 // restore scrolled section 292 Platform.runLater(() -> samplePageNavigation.setCurrentSection(scrolledSection)); 293 } 294 295 private void rebuildUI(boolean modena, boolean retina, int selectedTab, final SamplePage.Section scrolledSection) { 296 try { 297 if (root == null) { 298 root = new BorderPane(); 299 outerRoot.setCenter(root); 300 } else { 301 // clear out old UI 302 root.setTop(null); 303 root.setCenter(null); 304 } 305 // Create sample page and nav 306 samplePageNavigation = new SamplePageNavigation(); 307 samplePage = samplePageNavigation.getSamplePage(); 308 // Create Content Area 309 contentTabs = new TabPane(); 310 Tab tab1 = new Tab("All Controls"); 311 tab1.setContent(samplePageNavigation); 312 Tab tab2 = new Tab("UI Mosaic"); 313 tab2.setContent(new ScrollPane(mosaic = (Node)FXMLLoader.load(Modena.class.getResource("ui-mosaic.fxml")))); 314 315 Tab tab3 = new Tab("Alignment Test"); 316 tab3.setContent(new ScrollPane(heightTest = 317 (Node)FXMLLoader.load(Modena.class.getResource("SameHeightTest.fxml")))); 318 319 Tab tab4 = new Tab("Simple Windows"); 320 tab4.setContent(new ScrollPane(simpleWindows = new SimpleWindowPage())); 321 322 Tab tab5 = new Tab("Combinations"); 323 tab5.setContent(new ScrollPane(combinationsTest = 324 (Node)FXMLLoader.load(Modena.class.getResource("CombinationTest.fxml")))); 325 326 // Customer example from bug report http://javafx-jira.kenai.com/browse/DTL-5561 327 Tab tab6 = new Tab("Customer Example"); 328 tab6.setContent(new ScrollPane(customerTest = 329 (Node)FXMLLoader.load(Modena.class.getResource("ScottSelvia.fxml")))); 330 331 contentTabs.getTabs().addAll(tab1, tab2, tab3, tab4, tab5, tab6); 332 contentTabs.getSelectionModel().select(selectedTab); 333 samplePage.setMouseTransparent(test); 334 // height test set selection for 335 Platform.runLater(() -> { 336 for (Node n: heightTest.lookupAll(".choice-box")) { 337 ((ChoiceBox)n).getSelectionModel().selectFirst(); 338 } 339 for (Node n: heightTest.lookupAll(".combo-box")) { 340 ((ComboBox)n).getSelectionModel().selectFirst(); 341 } 342 }); 343 // Create Toolbar 344 retinaButton = new ToggleButton("@2x"); 345 retinaButton.setSelected(retina); 346 retinaButton.setOnAction(event -> { 347 ToggleButton btn = (ToggleButton)event.getSource(); 348 setRetinaMode(btn.isSelected()); 349 }); 350 ToggleGroup themesToggleGroup = new ToggleGroup(); 351 modenaButton = new ToggleButton("Modena"); 352 modenaButton.setToggleGroup(themesToggleGroup); 353 modenaButton.setSelected(modena); 354 modenaButton.setOnAction(rebuild); 355 modenaButton.getStyleClass().add("left-pill:"); 356 ToggleButton caspianButton = new ToggleButton("Caspian"); 357 caspianButton.setToggleGroup(themesToggleGroup); 358 caspianButton.setSelected(!modena); 359 caspianButton.setOnAction(rebuild); 360 caspianButton.getStyleClass().add("right-pill"); 361 Button reloadButton = new Button("", new ImageView(new Image(Modena.class.getResource("reload_12x14.png").toString()))); 362 reloadButton.setOnAction(rebuild); 363 364 rtlButton = new ToggleButton("RTL"); 365 rtlButton.setOnAction(event -> root.setNodeOrientation(rtlButton.isSelected() ? 366 NodeOrientation.RIGHT_TO_LEFT : NodeOrientation.LEFT_TO_RIGHT)); 367 368 embeddedPerformanceButton = new ToggleButton("EP"); 369 embeddedPerformanceButton.setSelected(embeddedPerformanceMode); 370 embeddedPerformanceButton.setTooltip(new Tooltip("Apply Embedded Performance extra stylesheet")); 371 embeddedPerformanceButton.setOnAction(event -> { 372 embeddedPerformanceMode = embeddedPerformanceButton.isSelected(); 373 rebuild.handle(event); 374 }); 375 376 Button saveButton = new Button("Save..."); 377 saveButton.setOnAction(saveBtnHandler); 378 379 Button restartButton = new Button("Restart"); 380 retinaButton.setOnAction(event -> restart()); 381 382 ToolBar toolBar = new ToolBar(new HBox(modenaButton, caspianButton), reloadButton, rtlButton, 383 embeddedPerformanceButton, new Separator(), retinaButton, 384 new Label("Base:"), 385 createBaseColorPicker(), 386 new Label("Background:"), 387 createBackgroundColorPicker(), 388 new Label("Accent:"), 389 createAccentColorPicker(), 390 new Separator(), saveButton, restartButton 391 ); 392 toolBar.setId("TestAppToolbar"); 393 // Create content group used for scaleing @2x 394 final Pane contentGroup = new Pane() { 395 @Override protected void layoutChildren() { 396 double scale = contentTabs.getTransforms().isEmpty() ? 1 : ((Scale)contentTabs.getTransforms().get(0)).getX(); 397 contentTabs.resizeRelocate(0,0,getWidth()/scale, getHeight()/scale); 398 } 399 }; 400 contentGroup.getChildren().add(contentTabs); 401 // populate root 402 root.setTop(toolBar); 403 root.setCenter(contentGroup); 404 405 samplePage.getStyleClass().add("needs-background"); 406 mosaic.getStyleClass().add("needs-background"); 407 heightTest.getStyleClass().add("needs-background"); 408 combinationsTest.getStyleClass().add("needs-background"); 409 customerTest.getStyleClass().add("needs-background"); 410 simpleWindows.setModena(modena); 411 // apply retina scale 412 if (retina) { 413 contentTabs.getTransforms().setAll(new Scale(2,2)); 414 } 415 root.applyCss(); 416 // update state 417 Platform.runLater(() -> { 418 // move focus out of the way 419 modenaButton.requestFocus(); 420 samplePageNavigation.setCurrentSection(scrolledSection); 421 }); 422 } catch (IOException ex) { 423 Logger.getLogger(Modena.class.getName()).log(Level.SEVERE, null, ex); 424 } 425 } 426 427 public RadioMenuItem buildFontRadioMenuItem(String name, final String in_fontName, final int in_fontSize, ToggleGroup tg) { 428 RadioMenuItem rmItem = new RadioMenuItem(name); 429 rmItem.setOnAction(event -> setFont(in_fontName, in_fontSize)); 430 rmItem.setStyle("-fx-font: " + in_fontSize + "px \"" + in_fontName + "\";"); 431 rmItem.setToggleGroup(tg); 432 return rmItem; 433 } 434 435 public void setFont(String in_fontName, int in_fontSize) { 436 System.out.println("==================================================================="); 437 System.out.println("== SETTING FONT TO "+in_fontName+" "+in_fontSize+"px"); 438 System.out.println("==================================================================="); 439 fontName = in_fontName; 440 fontSize = in_fontSize; 441 updateUserAgentStyleSheet(); 442 } 443 444 private ColorPicker createBaseColorPicker() { 445 ColorPicker colorPicker = new ColorPicker(Color.TRANSPARENT); 446 colorPicker.getCustomColors().addAll( 447 Color.TRANSPARENT, 448 Color.web("#f3622d"), 449 Color.web("#fba71b"), 450 Color.web("#57b757"), 451 Color.web("#41a9c9"), 452 Color.web("#888"), 453 Color.RED, 454 Color.ORANGE, 455 Color.YELLOW, 456 Color.GREEN, 457 Color.CYAN, 458 Color.BLUE, 459 Color.PURPLE, 460 Color.MAGENTA, 461 Color.BLACK 462 ); 463 colorPicker.valueProperty().addListener((observable, oldValue, c) -> setBaseColor(c)); 464 return colorPicker; 465 } 466 467 public void setBaseColor(Color c) { 468 if (c == null) { 469 baseColor = null; 470 } else { 471 baseColor = c; 472 } 473 updateUserAgentStyleSheet(); 474 } 475 476 private ColorPicker createBackgroundColorPicker() { 477 ColorPicker colorPicker = new ColorPicker(Color.TRANSPARENT); 478 colorPicker.getCustomColors().addAll( 479 Color.TRANSPARENT, 480 Color.web("#f3622d"), 481 Color.web("#fba71b"), 482 Color.web("#57b757"), 483 Color.web("#41a9c9"), 484 Color.web("#888"), 485 Color.RED, 486 Color.ORANGE, 487 Color.YELLOW, 488 Color.GREEN, 489 Color.CYAN, 490 Color.BLUE, 491 Color.PURPLE, 492 Color.MAGENTA, 493 Color.BLACK 494 ); 495 colorPicker.valueProperty().addListener((observable, oldValue, c) -> { 496 if (c == null) { 497 backgroundColor = null; 498 } else { 499 backgroundColor = c; 500 } 501 updateUserAgentStyleSheet(); 502 }); 503 return colorPicker; 504 } 505 506 private ColorPicker createAccentColorPicker() { 507 ColorPicker colorPicker = new ColorPicker(Color.web("#0096C9")); 508 colorPicker.getCustomColors().addAll( 509 Color.TRANSPARENT, 510 Color.web("#0096C9"), 511 Color.web("#4fb6d6"), 512 Color.web("#f3622d"), 513 Color.web("#fba71b"), 514 Color.web("#57b757"), 515 Color.web("#41a9c9"), 516 Color.web("#888"), 517 Color.RED, 518 Color.ORANGE, 519 Color.YELLOW, 520 Color.GREEN, 521 Color.CYAN, 522 Color.BLUE, 523 Color.PURPLE, 524 Color.MAGENTA, 525 Color.BLACK 526 ); 527 colorPicker.valueProperty().addListener((observable, oldValue, c) -> setAccentColor(c)); 528 return colorPicker; 529 } 530 531 public void setAccentColor(Color c) { 532 if (c == null) { 533 accentColor = null; 534 } else { 535 accentColor = c; 536 } 537 updateUserAgentStyleSheet(); 538 } 539 540 private EventHandler<ActionEvent> saveBtnHandler = event -> { 541 FileChooser fc = new FileChooser(); 542 fc.getExtensionFilters().add(new FileChooser.ExtensionFilter("PNG", "*.png")); 543 File file = fc.showSaveDialog(mainStage); 544 if (file != null) { 545 try { 546 samplePage.getStyleClass().add("root"); 547 int width = (int)(samplePage.getLayoutBounds().getWidth()+0.5d); 548 int height = (int)(samplePage.getLayoutBounds().getHeight()+0.5d); 549 BufferedImage imgBuffer = new BufferedImage(width,height,BufferedImage.TYPE_INT_ARGB); 550 Graphics2D g2 = imgBuffer.createGraphics(); 551 for (int y=0; y<height; y+=2048) { 552 SnapshotParameters snapshotParameters = new SnapshotParameters(); 553 int remainingHeight = Math.min(2048, height - y); 554 snapshotParameters.setViewport(new Rectangle2D(0,y,width,remainingHeight)); 555 WritableImage img = samplePage.snapshot(snapshotParameters, null); 556 g2.drawImage(SwingFXUtils.fromFXImage(img,null),0,y,null); 557 } 558 g2.dispose(); 559 ImageIO.write(imgBuffer, "PNG", file); 560 System.out.println("Written image: "+file.getAbsolutePath()); 561 } catch (IOException ex) { 562 Logger.getLogger(Modena.class.getName()).log(Level.SEVERE, null, ex); 563 } 564 } 565 }; 566 567 public static void main(String[] args) { 568 launch(args); 569 } 570 571 /** Utility method to load a URL into a string */ 572 private static String loadUrl(String url) { 573 StringBuilder sb = new StringBuilder(); 574 try { 575 BufferedReader br = new BufferedReader(new InputStreamReader(new URL(url).openStream())); 576 String line; 577 while ((line = br.readLine()) != null) { 578 sb.append(line); 579 sb.append('\n'); 580 } 581 } catch (IOException ex) { 582 Logger.getLogger(Modena.class.getName()).log(Level.SEVERE, null, ex); 583 } 584 return sb.toString(); 585 } 586 587 // ========================================================================= 588 // URL Handler to create magic "internal:stylesheet.css" url for our css string buffer 589 { 590 URL.setURLStreamHandlerFactory(new StringURLStreamHandlerFactory()); 591 } 592 593 private String colorToRGBA(Color color) { 594 // Older version didn't care about opacity 595 // return String.format((Locale) null, "#%02x%02x%02x", 596 // Math.round(color.getRed() * 255), 597 // Math.round(color.getGreen() * 255), 598 // Math.round(color.getBlue() * 255)); 599 return String.format((Locale) null, "rgba(%d, %d, %d, %f)", 600 (int) Math.round(color.getRed() * 255), 601 (int) Math.round(color.getGreen() * 255), 602 (int) Math.round(color.getBlue() * 255), 603 color.getOpacity()); 604 } 605 606 /** 607 * Simple URLConnection that always returns the content of the cssBuffer 608 */ 609 private class StringURLConnection extends URLConnection { 610 public StringURLConnection(URL url){ 611 super(url); 612 } 613 614 @Override public void connect() throws IOException {} 615 616 @Override public InputStream getInputStream() throws IOException { 617 return new ByteArrayInputStream(styleSheetContent.getBytes("UTF-8")); 618 } 619 } 620 621 private class StringURLStreamHandlerFactory implements URLStreamHandlerFactory { 622 URLStreamHandler streamHandler = new URLStreamHandler(){ 623 @Override protected URLConnection openConnection(URL url) throws IOException { 624 if (url.toString().toLowerCase().endsWith(".css")) { 625 return new StringURLConnection(url); 626 } else { 627 return new URL(styleSheetBase+url.getFile()).openConnection(); 628 } 629 } 630 }; 631 @Override public URLStreamHandler createURLStreamHandler(String protocol) { 632 if ("internal".equals(protocol)) { 633 return streamHandler; 634 } 635 return null; 636 } 637 } 638 }