com.codename1.designer.css.CSSTheme Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Codename One designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Codename One through http://www.codenameone.com/ if you
* need additional information or have any questions.
*/
package com.codename1.designer.css;
import com.codename1.io.JSONParser;
import com.codename1.io.Util;
import com.codename1.processing.Result;
import com.codename1.ui.Component;
import com.codename1.ui.Display;
import com.codename1.ui.EditorTTFFont;
import com.codename1.ui.EncodedImage;
import com.codename1.ui.Font;
import com.codename1.ui.Image;
import com.codename1.ui.animations.AnimationAccessor;
import com.codename1.ui.plaf.Accessor;
import com.codename1.ui.plaf.CSSBorder;
import com.codename1.ui.plaf.RoundBorder;
import com.codename1.ui.plaf.RoundRectBorder;
import com.codename1.ui.plaf.Style;
import com.codename1.ui.util.EditableResourcesForCSS;
import com.codename1.ui.util.EditableResources;
import com.codename1.ui.util.Resources;
import java.io.ByteArrayInputStream;
import java.io.CharArrayReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.stream.ImageInputStream;
import org.w3c.css.sac.AttributeCondition;
import org.w3c.css.sac.CSSException;
import org.w3c.css.sac.CSSParseException;
import org.w3c.css.sac.Condition;
import org.w3c.css.sac.ConditionalSelector;
import org.w3c.css.sac.DocumentHandler;
import org.w3c.css.sac.ElementSelector;
import org.w3c.css.sac.ErrorHandler;
import org.w3c.css.sac.InputSource;
import org.w3c.css.sac.LexicalUnit;
import org.w3c.css.sac.Parser;
import org.w3c.css.sac.SACMediaList;
import org.w3c.css.sac.Selector;
import org.w3c.css.sac.SelectorList;
import org.w3c.css.sac.SimpleSelector;
import org.w3c.css.sac.helpers.ParserFactory;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.flute.parser.ParseException;
/**
*
* @author shannah
*/
public class CSSTheme {
private boolean refreshImages;
URL baseURL;
File cssFile = new File("test.css");
File resourceFile = new File("test.css.res");
Element anyNodeStyle = new Element();
Map elements = new LinkedHashMap();
Map constants = new LinkedHashMap();
EditableResources res;
private String themeName = "Theme";
private ImagesMetadata imagesMetadata = new ImagesMetadata();
private List fontFaces = new ArrayList();
public static final int DEFAULT_TARGET_DENSITY = com.codename1.ui.Display.DENSITY_HD;
public static final String[] supportedNativeBorderTypes = new String[]{
"none",
"line",
"bevel",
"etched",
"solid"
};
public static boolean isBorderTypeNativelySupported(String type) {
for (String str : supportedNativeBorderTypes) {
if (str.equals(type)) {
return true;
}
}
return false;
}
private int targetDensity = DEFAULT_TARGET_DENSITY;
private double minDpi = 120;
private double maxDpi = 480;
private double currentDpi = 480;
private static class XYVal {
double x;
double y;
String axis;
boolean valid;
}
static boolean isGradient(LexicalUnit background) {
if (background != null) {
if (background.getLexicalUnitType() == LexicalUnit.SAC_FUNCTION && "linear-gradient".equals(background.getFunctionName())) {
return true;
} else if (background.getLexicalUnitType() == LexicalUnit.SAC_FUNCTION && "radial-gradient".equals(background.getFunctionName())) {
return true;
}
}
return false;
}
class ImageMetadata {
private String imageName;
int sourceDpi;
public ImageMetadata(String imageName, int sourceDpi) {
this.imageName = imageName;
this.sourceDpi = sourceDpi;
}
}
class ImagesMetadata {
private Map images = new LinkedHashMap<>();
public void addImageMetadata(ImageMetadata data) {
images.put(data.imageName, data);
}
public ImageMetadata get(String name) {
return images.get(name);
}
void load(Resources res) throws IOException {
images.clear();
String metadataStr = (String)res.getTheme(themeName).getOrDefault("@imageMetadata", "{}");
JSONParser parser = new JSONParser();
Map metadataMap = parser.parseJSON(new StringReader(metadataStr));
for (String key : (Set)metadataMap.keySet()) {
Map data = (Map)metadataMap.get(key);
int sourceDpi = (int)Math.round((data.containsKey("sourceDpi") ? ((Number)data.get("sourceDpi")).intValue() :
currentDpi));
String name = (String)data.get("imageName");
ImageMetadata md = new ImageMetadata(name, sourceDpi);
addImageMetadata(md);
}
}
void store(EditableResources res) {
Map map = new LinkedHashMap();
for (String key : images.keySet()) {
ImageMetadata md = images.get(key);
Map data = new LinkedHashMap();
data.put("imageName", md.imageName);
data.put("sourceDpi", md.sourceDpi);
map.put(md.imageName, data);
}
res.setThemeProperty(themeName, "@imageMetadata", Result.fromContent(map).toString());
}
}
static class CN1Gradient {
/**
* One of {@link Style#BACKGROUND_GRADIENT_LINEAR_HORIZONTAL}, {@link Style#BACKGROUND_GRADIENT_LINEAR_VERTICAL}, or
* {@link Style#BACKGROUND_GRADIENT_RADIAL}.
*/
int type;
int startColor;
int endColor;
float gradientX;
float gradientY;
float size;
byte bgTransparency;
String reason;
/**
* Flag to indicate whether this gradient is valid
* Only gradients that can be completely reproduced using CN1
* are valid. E.g. if the opacity of the start color is different than
* the end color, or there are multiple stages, or other parameters
* that can't be expressed as a CN1 gradient style, then this gradient won't
* be used and a 9-piece border will be generated as a fallback.
*/
boolean valid;
void parse(ScaledUnit background) {
if (background != null) {
if (background.getLexicalUnitType() == LexicalUnit.SAC_FUNCTION && "linear-gradient".equals(background.getFunctionName())) {
parseLinearGradient(background);
} else if (background.getLexicalUnitType() == LexicalUnit.SAC_FUNCTION && "radial-gradient".equals(background.getFunctionName())) {
parseRadialGradient(background);
}
}
}
private XYVal parseTransformCoordVal(String val) {
XYVal out = new XYVal();
switch (val) {
case "center" : out.x = 0.5; out.y = 0.5; break;
case "left" : out.x = 0; out.y = -1; out.axis = "x"; break;
case "right" : out.x = 1; out.y = -1; out.axis = "x"; break;
case "bottom" : out.x = -1;; out.y = 1; out.axis = "y"; break;
case "top" : out.x = -1; out.y = 0; out.axis = "y"; break;
default: return out;
}
out.valid = true;
return out;
}
private XYVal parseTransformCoordVal(double val, boolean x) {
XYVal out = new XYVal();
if (x) {
out.y = 0.5;
out.x = val / 100;
out.axis = "x";
} else {
out.y = val / 100;
out.x = 0.5;
out.axis = "y";
}
out.valid = true;
return out;
}
private void parseRadialGradient(ScaledUnit background) {
if (background == null || background.getLexicalUnitType() != LexicalUnit.SAC_FUNCTION || !"radial-gradient".equals(background.getFunctionName())) {
return;
}
ScaledUnit params = (ScaledUnit)background.getParameters();
if (params == null) {
reason = "No parameters found in radial-gradient() function [1]";
return;
}
ScaledUnit param1 = params;
ScaledUnit param2 = null;
double relX = 0.5;
double relY = 0.5;
double relSize = 1.0;
int step = 0;
while (param1 != null) {
// Parse the first parameter of radial-gradient
switch (param1.getLexicalUnitType()) {
case LexicalUnit.SAC_IDENT: {
switch (param1.getStringValue()) {
case "circle": {
if (step != 0) {
reason = "Unrecognized syntax for radial gradient [2]";
return;
}
step++;
ScaledUnit nex = (ScaledUnit)param1.getNextLexicalUnit();
if (nex == null) {
reason = "Unrecognized syntax for radial gradient [3]";
return;
}
if (nex.getLexicalUnitType() == LexicalUnit.SAC_OPERATOR_COMMA) {
// It's a circle at center
param1 = null;
param2 = (ScaledUnit)nex.getNextLexicalUnit();
break;
}
param1 = nex;
//reason = "Unsupported parameter following 'circle' in radial gradient.";
//return;
break;
}
case "closest-side" : {
// This is the only extent value that is supported.
step++;
param1 = (ScaledUnit)param1.getNextLexicalUnit();
if (param1 != null && param1.getLexicalUnitType() == LexicalUnit.SAC_OPERATOR_COMMA) {
param1 = null;
}
break;
}
case "at" : {
ScaledUnit nex = (ScaledUnit)param1.getNextLexicalUnit();
if (nex == null) {
reason = "Invalid syntax for radial-gradient position. 'at' followed by nothing. [4]";
return;
}
if (nex.getLexicalUnitType() == LexicalUnit.SAC_IDENT || nex.getLexicalUnitType() == LexicalUnit.SAC_PERCENTAGE) {
XYVal val, val2;
if (nex.getLexicalUnitType() == LexicalUnit.SAC_PERCENTAGE) {
val = parseTransformCoordVal(nex.getNumericValue(), true);
} else {
val = parseTransformCoordVal(nex.getStringValue());
}
if (!val.valid) {
reason = "Invalid position coordinate for radial-gradient. [5]";
return;
}
if ("x".equals(val.axis)) {
relX = val.x;
} else if ("y".equals(val.axis)) {
relY = val.y;
} else {
relX = val.x;
relY = val.y;
}
nex = (ScaledUnit)nex.getNextLexicalUnit();
if (nex == null) {
reason = "Invalid radial-gradient syntax. No color information provided. [6]";
return;
}
if (nex.getLexicalUnitType() == LexicalUnit.SAC_OPERATOR_COMMA) {
// we have our position.
param2 = (ScaledUnit)nex.getNextLexicalUnit();
param1 = null;
break;
}
if (nex.getLexicalUnitType() == LexicalUnit.SAC_IDENT || nex.getLexicalUnitType() == LexicalUnit.SAC_PERCENTAGE) {
if (nex.getLexicalUnitType() == LexicalUnit.SAC_PERCENTAGE) {
val2 = parseTransformCoordVal(nex.getNumericValue(), "y".equals(val.axis));
} else {
val2 = parseTransformCoordVal(nex.getStringValue());
}
if ("x".equals(val2.axis)) {
relX = val2.x;
} else if ("y".equals(val2.axis)) {
relY = val2.y;
} else if ("x".equals(val.axis)) {
relY = val2.y;
} else if ("y".equals(val.axis)) {
relX = val2.x;
} else {
relY = val2.y;
}
}
nex = (ScaledUnit)nex.getNextLexicalUnit();
if (nex == null || nex.getLexicalUnitType() != LexicalUnit.SAC_OPERATOR_COMMA) {
reason = "Invalid radial-gradient syntax. No color information provided. [7]";
return;
}
param1 = null;
param2 = (ScaledUnit) nex.getNextLexicalUnit();
break;
}
break;
}
default:
reason = "Unsupported syntax for radial-gradient. ("+param1.getStringValue()+") [8]";
return;
}
}
}
}
if (param2 == null) {
reason = "No color information provided (param2==null) [9]";
return;
}
Integer color1 = null;
Integer color2 = null;
int alpha = 0;
while (param2 != null) {
// parse 2nd param of radial-gradient
if (color1 == null) {
color1 = getColorInt(param2);
alpha = getColorAlphaInt(param2);
ScaledUnit nex = (ScaledUnit)param2.getNextLexicalUnit();
if (nex == null) {
reason = "Invalid radial gradient syntax. Missing 2nd color [11]";
return;
}
if (nex.getLexicalUnitType() == LexicalUnit.SAC_PERCENTAGE) {
if (Math.abs(nex.getNumericValue()) > 0.0001) {
reason = "CN1 native gradients must have start color at 0%. Falling back to use image background. [12]";
return;
}
nex = (ScaledUnit)nex.getNextLexicalUnit();
}
if (nex == null || nex.getLexicalUnitType()!= LexicalUnit.SAC_OPERATOR_COMMA) {
reason = "Invalid radial gradient syntax. Missing 2nd color";
return;
}
param2 = (ScaledUnit)nex.getNextLexicalUnit();
} else if (color2 == null) {
color2 = getColorInt(param2);
int alpha2 = getColorAlphaInt(param2);
if (alpha2 != alpha) {
reason = "Codename One only native supports gradients with the same alpha for start and end colors [13]";
return;
}
ScaledUnit nex = (ScaledUnit)param2.getNextLexicalUnit();
if (nex == null) {
break;
} else if (nex.getLexicalUnitType() == LexicalUnit.SAC_PERCENTAGE) {
relSize = nex.getNumericValue() / 100;
}
nex = (ScaledUnit)nex.getNextLexicalUnit();
// This should be the last parameter
if (nex != null) {
reason = "Only radial gradients with two color stops are supported natively in CN1. Falling back to image background. [14]";
return;
}
param2 = null;
}
}
if (color1 == null || color2 == null) {
reason = "Failed to set either start or end color when parsing radial-gradient [15]";
return;
}
type = Style.BACKGROUND_GRADIENT_RADIAL;
gradientX = 1-(float)relX;
gradientY = 1-(float)relY;
this.bgTransparency = (byte)alpha;
this.startColor = color1;
this.endColor = color2;
this.size = (float)relSize;
this.valid = true;
}
private void parseLinearGradient(ScaledUnit background) {
ScaledUnit linearGradientFunc = null;
int gradientType = 0;
boolean reverse = false;
Integer startColor = null;
Integer endColor = null;
int alpha = -1;
loop: while (background != null) {
switch (background.getLexicalUnitType()) {
case LexicalUnit.SAC_FUNCTION:
if ("linear-gradient".equals(background.getFunctionName())) {
linearGradientFunc = background;
break loop;
}
}
background = (ScaledUnit)background.getNextLexicalUnit();
}
if (linearGradientFunc == null) {
return;
}
ScaledUnit params = (ScaledUnit)linearGradientFunc.getParameters();
if (params == null) {
return;
}
ScaledUnit param1 = params;
switch (param1.getLexicalUnitType()) {
case LexicalUnit.SAC_IDENT: {
String identVal = param1.getStringValue();
if ("to".equals(identVal)) {
param1 = (ScaledUnit)param1.getNextLexicalUnit();
if (param1 == null) {
reason = "Invalid linear-gradient syntax. 'to' must be followed by a side.";
return;
}
if (param1.getLexicalUnitType() == LexicalUnit.SAC_IDENT) {
String sideStr = param1.getStringValue();
switch (sideStr) {
case "top" : {
gradientType = Style.BACKGROUND_GRADIENT_LINEAR_VERTICAL;
reverse = true;
break;
}
case "bottom" : {
gradientType = Style.BACKGROUND_GRADIENT_LINEAR_VERTICAL;
break;
}
case "left" : {
gradientType = Style.BACKGROUND_GRADIENT_LINEAR_HORIZONTAL;
reverse = true;
break;
}
case "right": {
gradientType = Style.BACKGROUND_GRADIENT_LINEAR_HORIZONTAL;
break;
}
default :
reason = "Unrecognized side identifier '"+sideStr+"'. Expected top, left, bottom, or right";
return;
}
} else {
reason = "Unsupported syntax following to ident in linear-gradient.";
return;
}
} else {
reason = "Unsupported syntax for first parameter of linear-gradient. Only 'to' and 'degree' values are supported";
return;
}
break;
}
case LexicalUnit.SAC_RADIAN:
case LexicalUnit.SAC_DEGREE: {
double degreeVal = params.getNumericValue();
degreeVal = (params.getLexicalUnitType() == LexicalUnit.SAC_RADIAN) ?
(degreeVal * 180.0/Math.PI) : degreeVal;
if (Math.abs(degreeVal) < 0.0001) {
gradientType = Style.BACKGROUND_GRADIENT_LINEAR_VERTICAL;
reverse = true; //bottom to top
} else if (Math.abs(degreeVal - 90) < 0.0001 ) {
gradientType = Style.BACKGROUND_GRADIENT_LINEAR_HORIZONTAL;
} else if (Math.abs(degreeVal - 180) < 0.0001) {
gradientType = Style.BACKGROUND_GRADIENT_LINEAR_VERTICAL;
} else if (Math.abs(degreeVal - 270) < 0.0001) {
gradientType = Style.BACKGROUND_GRADIENT_LINEAR_HORIZONTAL;
reverse = true;
} else {
reason = "Only 0, 90, 180, and 270 degrees supported.";
return;
}
break;
}
default:
System.err.println("Expected degree. Found lexical type" +param1.getLexicalUnitType());
System.err.println("Currently only degree and radian parameters supported for first arg of linear-gradient");
// We only support degree and radian first param right now.
return;
}
ScaledUnit param2 = (ScaledUnit)param1.getNextLexicalUnit();
if (param2 == null) {
return;
}
if (param2.getLexicalUnitType() == LexicalUnit.SAC_OPERATOR_COMMA) {
param2 = (ScaledUnit)param2.getNextLexicalUnit();
}
if (param2 == null) {
return;
}
switch (param2.getLexicalUnitType()) {
case LexicalUnit.SAC_IDENT:
case LexicalUnit.SAC_FUNCTION:
case LexicalUnit.SAC_RGBCOLOR:
case LexicalUnit.SAC_ATTR:
if (reverse) {
endColor = getColorInt(param2);
} else {
startColor = getColorInt(param2);
}
alpha = getColorAlphaInt(param2);
break;
default:
System.err.println("Expected color for 2nd parameter of linear-gradient");
return;
}
ScaledUnit param3 = (ScaledUnit)param2.getNextLexicalUnit();
if (param3 == null) {
return;
}
if (param3.getLexicalUnitType() == LexicalUnit.SAC_OPERATOR_COMMA) {
param3 = (ScaledUnit)param3.getNextLexicalUnit();
}
if (param3 == null) {
return;
}
switch (param3.getLexicalUnitType()) {
case LexicalUnit.SAC_IDENT:
case LexicalUnit.SAC_FUNCTION:
case LexicalUnit.SAC_RGBCOLOR:
case LexicalUnit.SAC_ATTR:
if (reverse) {
startColor = getColorInt(param3);
} else {
endColor = getColorInt(param3);
}
int alpha2 = getColorAlphaInt(param3);
if (alpha2 != alpha) {
// Alphas don't match so the gradient can't be expressed using CN1 gradients.
reason = "alphas of start and end colors don't match";
return;
}
break;
default:
System.err.println("Error processing background rule "+background+" for selector "+currentId);
System.err.println("Expecting color for param 3 of linear-gradient but found "+param3+" of type "+param3.getLexicalUnitType());
System.err.println("This gradient will need to be generated as an image border, which will slow down CSS compilation.");
//new RuntimeException("Error processing background rule "+background).printStackTrace(System.err);
return;
}
this.startColor = startColor;
this.endColor = endColor;
this.type = gradientType;
this.bgTransparency = (byte)alpha;
this.valid = true;
/*
Object[] out = new Object[] {
startColor,
endColor,
0f,
0f,
0f
};
*/
}
Object[] getThemeBgGradient() {
if (!valid) {
return null;
}
return new Object[]{
startColor,
endColor,
gradientX,
gradientY,
size
};
}
byte getBgTransparency() {
if (valid) {
return 0;
}
return bgTransparency;
}
}
//private int currentScreenWidth=1080;
//private int currentScreenHeight=1920;
/*
LexicalUnit px(LexicalUnit val) {
switch (val.getLexicalUnitType()) {
case LexicalUnit.SAC_CENTIMETER:
case LexicalUnit.SAC_MILLIMETER:
case LexicalUnit.SAC_PIXEL:
}
return val;
}
*/
/*
double px(double val) {
switch (targetDensity) {
case com.codename1.ui.Display.DENSITY_MEDIUM:
return val;
// Generate High Resolution MultiImage
case com.codename1.ui.Display.DENSITY_HIGH:
return val*480.0/320.0;
// Generate Very High Resolution MultiImage
case com.codename1.ui.Display.DENSITY_VERY_HIGH:
return val*2.0;
// Generate HD Resolution MultiImage
case com.codename1.ui.Display.DENSITY_HD:
return val*1080.0/320.0;
// Generate HD560 Resolution MultiImage
case com.codename1.ui.Display.DENSITY_560:
return val*1500.0/320.0;
// Generate HD2 Resolution MultiImage
case com.codename1.ui.Display.DENSITY_2HD:
return val*2000.0/320.0;
// Generate 4k Resolution MultiImage
case com.codename1.ui.Display.DENSITY_4K:
return val*2500.0/320.0;
}
throw new RuntimeException("Unsupported density");
}
*/
public class FontFace {
File fontFile;
LexicalUnit fontFamily;
LexicalUnit src;
LexicalUnit fontWeight;
LexicalUnit fontStretch;
LexicalUnit fontStyle;
URL getURL() {
try {
if (src == null) {
return null;
}
switch (src.getLexicalUnitType()) {
case LexicalUnit.SAC_URI:
String url = src.getStringValue();
if (url.startsWith("github://")) {
//url(https://raw.githubusercontent.com/google/fonts/master/ofl/sourcesanspro/SourceSansPro-Light.ttf);
url = "https://raw.githubusercontent.com/" + url.substring(9).replace("/blob/master/", "/master/");
return new URL(url);
}
if (url.startsWith("http://") || url.startsWith("https://")) {
return new URL(url);
} else {
return new URL(baseURL, url);
}
}
return null;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
File getFontFile() {
if (fontFile == null) {
try {
URL url = getURL();
if (url == null) {
return null;
}
File parentDir = cssFile.getParentFile();
if (url.getProtocol().startsWith("http")) {
// If it is remote, check so see if we've already downloaded
// the font to the current directory.
String fontName = java.net.URLDecoder.decode(url.getPath(), "UTF-8");
if (fontName.indexOf("/") != -1) {
fontName = fontName.substring(fontName.lastIndexOf("/")+1);
}
File tmpFontFile = new File(parentDir, fontName);
if (tmpFontFile.exists()) {
fontFile = tmpFontFile;
} else {
InputStream is = url.openStream();
FileOutputStream fos = new FileOutputStream(tmpFontFile);
Util.copy(is, fos);
Util.cleanup(is);
Util.cleanup(fos);
fontFile = tmpFontFile;
}
} else if ("file".equals(url.getProtocol())){
fontFile = new File(url.toURI());
}
} catch (Exception ex) {
Logger.getLogger(CSSTheme.class.getName()).log(Level.SEVERE, null, ex);
throw new RuntimeException(ex);
}
}
if (fontFile != null && resourceFile != null) {
File srcDir = resourceFile.getParentFile();
File deployFontFile = new File(srcDir, fontFile.getName());
if (!deployFontFile.exists()) {
try (FileInputStream in = new FileInputStream(fontFile); FileOutputStream out = new FileOutputStream(deployFontFile)) {
Util.copy(in, out);
Util.cleanup(out);
} catch (IOException ioe) {
ioe.printStackTrace();
throw new RuntimeException(ioe);
}
}
}
return fontFile;
}
}
public FontFace createFontFace() {
FontFace f = new FontFace();
fontFaces.add(f);
return f;
}
private static class ScaledUnit implements LexicalUnit {
LexicalUnit src;
double dpi=144;
int screenWidth=640;
int screenHeight=960;
CN1Gradient gradient;
LexicalUnit next, prev, param;
ScaledUnit(LexicalUnit src, double dpi, int screenWidth, int screenHeight) {
this.src = src;
this.dpi = dpi;
this.screenWidth = screenWidth;
this.screenHeight = screenHeight;
}
public void setParameters(LexicalUnit params) {
this.param = params;
}
private boolean nextLexicalUnitNull;
public void setNextLexicalUnit(LexicalUnit next) {
this.next = next;
nextLexicalUnitNull = (next == null);
}
private boolean prevLexicalUnitNull;
public void setPrevLexicalUnit(LexicalUnit prev) {
this.prev = prev;
prevLexicalUnitNull = (prev == null);
}
/**
* If this lexical unit is a gradient function and can be expressed as a CN1 gradient
* this will return that gradient. Otherwise it will return null.
* @return
*/
public CN1Gradient getCN1Gradient() {
if (gradient == null && isGradient(src)) {
gradient = new CN1Gradient();
gradient.parse(this);
if (!gradient.valid && gradient.reason != null) {
System.err.println("Selector with id "+currentId+" Gradient not valid: "+gradient.reason);
}
}
if (gradient != null && gradient.valid) {
return gradient;
}
return null;
}
public boolean isCN1Gradient() {
CN1Gradient g = getCN1Gradient();
return (g != null && g.valid);
}
@Override
public short getLexicalUnitType() {
return src.getLexicalUnitType();
}
@Override
public LexicalUnit getNextLexicalUnit() {
if (next != null || nextLexicalUnitNull) return next;
LexicalUnit nex = src.getNextLexicalUnit();
LexicalUnit out = nex == null ? null : new ScaledUnit(nex, dpi, screenWidth, screenHeight);
if (out != null) next = out;
return out;
}
private static boolean hasCycle(LexicalUnit lu, ScaledUnit su) {
if (lu == su) return true;
if (su.src instanceof ScaledUnit) {
return hasCycle(lu, (ScaledUnit)su.src);
}
return false;
}
public ScaledUnit getNextNumericUnit() {
ScaledUnit nex = (ScaledUnit)getNextLexicalUnit();
while (nex != null) {
switch (nex.getLexicalUnitType()) {
case LexicalUnit.SAC_INTEGER:
case LexicalUnit.SAC_REAL:
return nex;
}
nex = (ScaledUnit)nex.getNextLexicalUnit();
}
return null;
}
@Override
public LexicalUnit getPreviousLexicalUnit() {
if (this.prev != null || prevLexicalUnitNull) return this.prev;
LexicalUnit prev = src.getPreviousLexicalUnit();
LexicalUnit out = prev == null ? null : new ScaledUnit(prev, dpi, screenWidth, screenHeight);
if (out != null) this.prev = out;
return out;
}
@Override
public int getIntegerValue() {
return src.getIntegerValue();
}
@Override
public float getFloatValue() {
return src.getFloatValue();
}
public double getNumericValue() {
switch (src.getLexicalUnitType()) {
case LexicalUnit.SAC_INTEGER:
return src.getIntegerValue();
default:
return src.getFloatValue();
}
}
@Override
public String getDimensionUnitText() {
return src.getDimensionUnitText();
}
@Override
public String getFunctionName() {
return src.getFunctionName();
}
@Override
public LexicalUnit getParameters() {
if (this.param != null) return this.param;
LexicalUnit param = src.getParameters();
LexicalUnit out = param == null ? null : new ScaledUnit(param, dpi, screenWidth, screenHeight);
if (out != null) this.param = out;
return out;
}
String renderAsCSSValue(double targetDpi, int targetScreenWidth, int targetScreenHeight) {
ScaledUnit lu = this;
if (lu == null) {
return "";
}
switch (lu.getLexicalUnitType()) {
case LexicalUnit.SAC_MILLIMETER:
case LexicalUnit.SAC_CENTIMETER:
return (lu.getFloatValue()*dpi/targetDpi)+lu.getDimensionUnitText();
case LexicalUnit.SAC_POINT:
return (lu.getFloatValue()*dpi/targetDpi)+"px";
case LexicalUnit.SAC_PIXEL:
case LexicalUnit.SAC_PERCENTAGE:
case LexicalUnit.SAC_DEGREE:
return lu.getFloatValue() + lu.getDimensionUnitText();
case LexicalUnit.SAC_URI:
return "url("+lu.getStringValue()+")";
case LexicalUnit.SAC_FUNCTION: {
StringBuilder sb = new StringBuilder();
String fname = lu.getFunctionName();
if ("cn1rgb".equals(fname)) {
fname = "rgb";
} else if ("cn1rgba".equals(fname)) {
fname = "rgba";
}
sb.append(fname).append("(");
ScaledUnit val = (ScaledUnit)lu.getParameters();
//sb.append(String.valueOf(val));
boolean empty = true;
while (val != null) {
empty = false;
sb.append(val.renderAsCSSValue(targetDpi, targetScreenWidth, targetScreenHeight)).append(" ");
val = (ScaledUnit)val.getNextLexicalUnit();
}
if (!empty) {
sb.setLength(sb.length()-1);
}
sb.append(")");
return sb.toString();
}
case LexicalUnit.SAC_OPERATOR_COMMA:
return ",";
case LexicalUnit.SAC_OPERATOR_SLASH:
return "/";
case LexicalUnit.SAC_IDENT:
return lu.getStringValue();
case LexicalUnit.SAC_STRING_VALUE:
return lu.getStringValue();
case LexicalUnit.SAC_RGBCOLOR:
StringBuilder sb = new StringBuilder();
sb.append("rgb(");
ScaledUnit val = (ScaledUnit)lu.getParameters();
while (val != null) {
sb.append(val.renderAsCSSValue(targetDpi, targetScreenWidth, targetScreenHeight));
val = (ScaledUnit)val.getNextLexicalUnit();
}
sb.append(")");
return sb.toString();
case LexicalUnit.SAC_INTEGER:
return String.valueOf(lu.getIntegerValue());
case LexicalUnit.SAC_REAL:
return String.valueOf(lu.getFloatValue());
default:
String unitText = null;
try {
unitText = lu.getDimensionUnitText();
} catch (Exception ex) {
break;
}
if (unitText != null) {
if ("rem".equals(unitText)) {
return lu.getFloatValue()+"rem";
} else if ("vw".equals(unitText)) {
return lu.getFloatValue()+"vw";
} else if ("vh".equals(unitText)) {
return lu.getFloatValue()+"vh";
} else if ("vmin".equals(unitText)) {
return lu.getFloatValue()+"vmin";
} else if ("vmax".equals(unitText)) {
return lu.getFloatValue()+"vmax";
}
}
}
throw new RuntimeException("Unsupported lex unit type "+lu.getLexicalUnitType());
}
@Override
public String getStringValue() {
return src.getStringValue();
}
@Override
public LexicalUnit getSubValues() {
LexicalUnit sv = src.getSubValues();
return sv == null ? null : new ScaledUnit(sv, dpi, screenWidth, screenHeight);
}
public int getPixelValue() {
return getPixelValue(dpi, screenWidth, screenHeight);
}
public int getPixelValue(int baseWidth, int baseHeight) {
return getPixelValue(dpi, baseWidth, baseHeight);
}
public int getPixelValue(double targetDpi) {
return getPixelValue(targetDpi, screenWidth, screenHeight);
}
public float getMMValue(double targetDpi) {
return getMMValue(targetDpi, screenWidth, screenHeight);
}
public float getMMValue() {
return getMMValue(dpi);
}
public float getMMValue(double targetDpi, int baseWidth, int baseHeight) {
switch (src.getLexicalUnitType()) {
case LexicalUnit.SAC_POINT:
return (float)this.getNumericValue() / 72f * 25.4f;
case LexicalUnit.SAC_PIXEL:
return (float)(this.getNumericValue() * 25.4/targetDpi);
case LexicalUnit.SAC_MILLIMETER:
return (float)this.getNumericValue();
case LexicalUnit.SAC_CENTIMETER:
return (float)this.getNumericValue() * 10;
case LexicalUnit.SAC_INTEGER:
case LexicalUnit.SAC_REAL:
return (float)(this.getNumericValue() * 25.4/targetDpi);
case LexicalUnit.SAC_PERCENTAGE:
return (float)((((double)baseWidth) * this.getNumericValue() / 100.0) * 25.4/targetDpi);
}
throw new RuntimeException("Cannot get mm value for type "+src);
}
public int getPixelValue(double targetDpi, int baseWidth, int baseHeight) {
switch (src.getLexicalUnitType()) {
case LexicalUnit.SAC_POINT:
return (int)(this.getNumericValue() * targetDpi / 160.0);
case LexicalUnit.SAC_PIXEL:
return (int)this.getNumericValue();
case LexicalUnit.SAC_MILLIMETER:
return (int)(this.getNumericValue() * targetDpi / 25.4);
case LexicalUnit.SAC_CENTIMETER:
return (int)(this.getNumericValue() * targetDpi / 2.54);
case LexicalUnit.SAC_INTEGER:
case LexicalUnit.SAC_REAL:
return (int)this.getNumericValue();
case LexicalUnit.SAC_PERCENTAGE:
return (int)(((double)baseWidth) * this.getNumericValue() / 100.0);
}
throw new RuntimeException("Cannot get pixel value for type "+src);
}
@Override
public String toString() {
return src.toString();
}
}
private class PixelUnit implements LexicalUnit {
float val;
PixelUnit(float val) {
this.val = val;
}
@Override
public short getLexicalUnitType() {
return LexicalUnit.SAC_PIXEL;
}
@Override
public LexicalUnit getNextLexicalUnit() {
return null;
}
@Override
public LexicalUnit getPreviousLexicalUnit() {
return null;
}
@Override
public int getIntegerValue() {
return (int)val;
}
@Override
public float getFloatValue() {
return val;
}
@Override
public String getDimensionUnitText() {
return "px";
}
@Override
public String getFunctionName() {
return null;
}
@Override
public LexicalUnit getParameters() {
return null;
}
@Override
public String getStringValue() {
return null;
}
@Override
public LexicalUnit getSubValues() {
return null;
}
}
String renderAsCSSString(LexicalUnit lu) {
if (lu == null) {
return "none";
}
return ((ScaledUnit)lu).renderAsCSSValue(160, 640, 960);
}
String renderCSSProperty(String property, Map styles) {
if (property.contains("padding") || property.contains("margin")) {
return "";
}
if ("opacity".equals(property)) {
// We don't render opacity. We let CN1 handle this using the opacity
// style property.
return "";
}
if (property.startsWith("cn1-")) {
switch (property) {
case "cn1-border-bottom-left-radius-x":
return "border-radius: "+renderAsCSSString(styles.get("cn1-border-top-left-radius-x")) + " " +
renderAsCSSString(styles.get("cn1-border-top-right-radius-x")) + " " +
renderAsCSSString(styles.get("cn1-border-bottom-right-radius-x")) + " " +
renderAsCSSString(styles.get("cn1-border-bottom-left-radius-x")) + " / " +
renderAsCSSString(styles.get("cn1-border-top-left-radius-y")) + " " +
renderAsCSSString(styles.get("cn1-border-top-right-radius-y")) + " " +
renderAsCSSString(styles.get("cn1-border-bottom-right-radius-y")) + " " +
renderAsCSSString(styles.get("cn1-border-bottom-left-radius-y"));
case "cn1-box-shadow-h" :
LexicalUnit h = styles.get("cn1-box-shadow-h");
if (h == null) {
return "";
}
if ("none".equals(h.getStringValue())) {
return "box-shadow: none";
} else {
return "box-shadow: " +
renderAsCSSString(styles.get("cn1-box-shadow-h")) + " " +
renderAsCSSString(styles.get("cn1-box-shadow-v")) + " " +
(((h = styles.get("cn1-box-shadow-blur")) != null) ? (renderAsCSSString(h) + " ") : "") +
(((h = styles.get("cn1-box-shadow-spread")) != null) ? (renderAsCSSString(h) + " ") : "") +
(((h = styles.get("cn1-box-shadow-color")) != null) ? (renderAsCSSString(h) + " ") : "") +
(((h = styles.get("cn1-box-shadow-inset")) != null && "inset".equals(h.getStringValue())) ? (renderAsCSSString(h) + " ") : "");
}
}
return "";
} else {
switch (property) {
case "width": {
LexicalUnit value = styles.get(property);
switch (value.getLexicalUnitType()) {
case LexicalUnit.SAC_PERCENTAGE:
return property + ":"+ (int)(value.getFloatValue() / 100f * 640f) + "px";
}
break;
}
case "height": {
LexicalUnit value = styles.get(property);
switch (value.getLexicalUnitType()) {
case LexicalUnit.SAC_PERCENTAGE:
return property + ":"+ (int)(value.getFloatValue() / 100f * 960f) + "px";
}
break;
}
case "border-image-slice": {
StringBuilder sb = new StringBuilder();
sb.append("border-image-slice: ");
LexicalUnit value = styles.get(property);
boolean first = true;
while (value != null) {
if (first) {
first = false;
} else {
sb.append(" ");
}
switch (value.getLexicalUnitType()) {
case LexicalUnit.SAC_PERCENTAGE:
sb.append(value.getFloatValue()+"%");
break;
case LexicalUnit.SAC_PIXEL:
sb.append(value.getFloatValue()+"px");
}
value = value.getNextLexicalUnit();
}
if (first) {
sb.append("none");
}
return sb.toString();
}
}
return property + ":"+renderAsCSSString(styles.get(property));
}
}
public String getHtmlPreview() {
StringBuilder sb = new StringBuilder();
sb.append("\n");
for (String name : elements.keySet()) {
Element el = (Element)elements.get(name);
sb.append("").append(name).append("
")
.append(el.getHtmlPreview())
.append("::Unselected
")
.append(el.getUnselected().getHtmlPreview())
.append("::Selected
")
.append(el.getSelected().getHtmlPreview())
.append("::Pressed
")
.append(el.getPressed().getHtmlPreview())
.append("::Disabled
")
.append(el.getDisabled().getHtmlPreview())
.append("
");
}
sb.append("");
return sb.toString();
}
public boolean requiresCaptureHtml() {
for (String name : elements.keySet()) {
if (!isModified(name)) {
continue;
}
Element el = (Element)elements.get(name);
Map unselectedStyle = el.getUnselected().getFlattenedStyle();
if (el.requiresBackgroundImageGeneration(unselectedStyle) || el.requiresImageBorder(unselectedStyle)) {
return true;
}
Map selectedStyle = el.getSelected().getFlattenedStyle();
if (el.requiresBackgroundImageGeneration(selectedStyle) || el.requiresImageBorder(selectedStyle)) {
return true;
}
Map pressedStyle = el.getPressed().getFlattenedStyle();
if (el.requiresBackgroundImageGeneration(pressedStyle) || el.requiresImageBorder(pressedStyle)) {
return true;
}
Map disabledStyle = el.getDisabled().getFlattenedStyle();
if (el.requiresBackgroundImageGeneration(disabledStyle) || el.requiresImageBorder(disabledStyle)) {
return true;
}
}
return false;
}
public String generateCaptureHtml() {
StringBuilder sb = new StringBuilder();
sb.append("\n"
+ "");
for (String name : elements.keySet()) {
if (!isModified(name)) {
continue;
}
Element el = (Element)elements.get(name);
Map unselectedStyle = el.getUnselected().getFlattenedStyle();
if (el.requiresBackgroundImageGeneration(unselectedStyle) || el.requiresImageBorder(unselectedStyle)) {
sb.append(el.getUnselected().getEmptyHtmlWithId(name, unselectedStyle));
}
Map selectedStyle = el.getSelected().getFlattenedStyle();
if (el.requiresBackgroundImageGeneration(selectedStyle) || el.requiresImageBorder(selectedStyle)) {
sb.append(el.getSelected().getEmptyHtmlWithId(name+".sel", selectedStyle));
}
Map pressedStyle = el.getPressed().getFlattenedStyle();
if (el.requiresBackgroundImageGeneration(pressedStyle) || el.requiresImageBorder(pressedStyle)) {
sb.append(el.getPressed().getEmptyHtmlWithId(name+".press", pressedStyle));
}
Map disabledStyle = el.getDisabled().getFlattenedStyle();
if (el.requiresBackgroundImageGeneration(disabledStyle) || el.requiresImageBorder(disabledStyle)) {
sb.append(el.getDisabled().getEmptyHtmlWithId(name+".dis", disabledStyle));
}
}
sb.append("");
return sb.toString();
}
public Map calculateSelectorChecksums() {
Map out = new LinkedHashMap();
for (String id : elements.keySet()) {
Element el = elements.get(id);
out.put(id, el.getChecksum());
}
return out;
}
public void saveSelectorChecksums(File output) throws FileNotFoundException, IOException {
try (ObjectOutputStream fos = new ObjectOutputStream(new FileOutputStream(output))) {
fos.writeObject(calculateSelectorChecksums());
}
}
public Map loadSelectorChecksums(File input) throws FileNotFoundException, IOException, ClassNotFoundException {
Map out;
try (ObjectInputStream fis = new ObjectInputStream(new FileInputStream(input))) {
out = (Map)fis.readObject();
}
return out;
}
public static enum CacheStatus {
UNCHANGED,
MODIFIED,
DELETED,
ADDED
}
Map selectorCacheStatus;
File selectorCacheFile;
public void loadSelectorCacheStatus(File cachedFile) throws IOException {
if (!cachedFile.equals(selectorCacheFile)) {
selectorCacheStatus = calculateSelectorCacheStatus(cachedFile);
selectorCacheFile = cachedFile;
}
}
public Map getCacheStatus(File cachedFile) throws IOException {
loadSelectorCacheStatus(cachedFile);
return selectorCacheStatus;
}
/**
* Checks if the given element ID has been modified since the last cache
* @param id
* @return
*/
public boolean isModified(String id) {
if (selectorCacheStatus != null) {
CacheStatus status = selectorCacheStatus.get(id);
if (status == null) {
return true;
}
switch (status) {
case MODIFIED:
case ADDED:
return true;
}
Element el = elements.get(id);
String derive = el.getThemeDerive(el.getFlattenedStyle(), "");
String unselectedDerive = el.getThemeDerive(el.getFlattenedUnselectedStyle(), "");
String selectedDerive = el.getThemeDerive(el.getFlattenedSelectedStyle(), "");
String pressedDerive = el.getThemeDerive(el.getFlattenedPressedStyle(), "");
String disabledDerive = el.getThemeDerive(el.getFlattenedDisabledStyle(), "");
if (derive != null && selectorCacheStatus.containsKey(derive) && isModified(derive)) {
return true;
}
if (unselectedDerive != null && selectorCacheStatus.containsKey(unselectedDerive) && isModified(unselectedDerive)) {
return true;
}
if (selectedDerive != null && selectorCacheStatus.containsKey(selectedDerive) && isModified(selectedDerive)) {
return true;
}
if (pressedDerive != null && selectorCacheStatus.containsKey(pressedDerive) && isModified(pressedDerive)) {
return true;
}
if (disabledDerive != null && selectorCacheStatus.containsKey(disabledDerive) && isModified(disabledDerive)) {
return true;
}
return false;
}
return true;
}
public Set getDeletedElements() {
HashSet out = new HashSet();
if (selectorCacheStatus != null) {
for (String id : selectorCacheStatus.keySet()) {
if (CacheStatus.DELETED.equals(selectorCacheStatus.get(id))) {
out.add(id);
}
}
}
return out;
}
private static String str(LexicalUnit lu, String defaultVal) {
if (lu == null) {
return defaultVal;
}
ScaledUnit su = (ScaledUnit)lu;
double numVal = su.getNumericValue();
String num = numVal+"";
if (Math.ceil(numVal) == Math.floor(numVal)) {
num = ((int)numVal)+"";
}
String unitText = "";
try {
unitText = lu.getDimensionUnitText();
} catch (Exception ex){} // This might throw an exception if there was no unit given, but we don't care.
switch (lu.getLexicalUnitType()) {
case LexicalUnit.SAC_MILLIMETER:
return num+"mm";
case LexicalUnit.SAC_INTEGER:
case LexicalUnit.SAC_REAL:
return num+"";
case LexicalUnit.SAC_POINT:
return num+"pt";
case LexicalUnit.SAC_PIXEL:
return ((int)Math.round(su.getNumericValue()))+"px";
case LexicalUnit.SAC_INCH:
return num+"in";
case LexicalUnit.SAC_EM:
return num+"em";
}
return num+unitText;
}
public Map calculateSelectorCacheStatus(File cachedFile) throws IOException {
try {
Map current = calculateSelectorChecksums();
Map previous = loadSelectorChecksums(cachedFile);
HashMap out = new LinkedHashMap();
if (previous == null) {
for (String id : current.keySet()) {
out.put(id, CacheStatus.ADDED);
}
return out;
} else {
for (String id : current.keySet()) {
if (!previous.containsKey(id)) {
out.put(id, CacheStatus.ADDED);
} else if (previous.get(id).equals(current.get(id))) {
out.put(id, CacheStatus.UNCHANGED);
} else {
out.put(id, CacheStatus.MODIFIED);
}
}
for (String id : previous.keySet()) {
if (!current.containsKey(id)) {
out.put(id, CacheStatus.DELETED);
}
}
return out;
}
} catch (ClassNotFoundException ex) {
throw new RuntimeException(ex);
}
}
public void updateResources() {
// TODO: We need to remove stale theme entries
// https://github.com/codenameone/CodenameOne/issues/2698
for (String id : elements.keySet()) {
if (!isModified(id)) {
continue;
}
String currToken = "";
try {
Element el = elements.get(id);
Map unselectedStyles = el.getUnselected().getFlattenedStyle();
Map selectedStyles = el.getSelected().getFlattenedStyle();
Map pressedStyles = el.getPressed().getFlattenedStyle();
Map disabledStyles = el.getDisabled().getFlattenedStyle();
Element selected = el.getSelected();
String selId = id+".sel";
String unselId = id;
String pressedId = id+".press";
String disabledId = id+".dis";
currToken = "padding";
res.setThemeProperty(themeName, unselId+".padding", el.getThemePadding(unselectedStyles));
currToken = "padUnit";
res.setThemeProperty(themeName, unselId+".padUnit", el.getThemePaddingUnit(unselectedStyles));
currToken = "selected padding";
res.setThemeProperty(themeName, selId+"#padding", el.getThemePadding(selectedStyles));
currToken = "selected padUnit";
res.setThemeProperty(themeName, selId+"#padUnit", el.getThemePaddingUnit(selectedStyles));
currToken = "pressed padding";
res.setThemeProperty(themeName, pressedId+"#padding", el.getThemePadding(pressedStyles));
currToken = "pressed padUnit";
res.setThemeProperty(themeName, pressedId+"#padUnit", el.getThemePaddingUnit(pressedStyles));
currToken = "disabled padding";
res.setThemeProperty(themeName, disabledId+"#padding", el.getThemePadding(disabledStyles));
currToken = "disabled padUnit";
res.setThemeProperty(themeName, disabledId+"#padUnit", el.getThemePaddingUnit(disabledStyles));
currToken = "margin";
res.setThemeProperty(themeName, unselId+".margin", el.getThemeMargin(unselectedStyles));
currToken = "marUnit";
res.setThemeProperty(themeName, unselId+".marUnit", el.getThemeMarginUnit(unselectedStyles));
currToken = "selected margin";
res.setThemeProperty(themeName, selId+"#margin", el.getThemeMargin(selectedStyles));
currToken = "selected marUnit";
res.setThemeProperty(themeName, selId+"#marUnit", el.getThemeMarginUnit(selectedStyles));
currToken = "pressed margin";
res.setThemeProperty(themeName, pressedId+"#margin", el.getThemeMargin(pressedStyles));
currToken = "pressed marUnit";
res.setThemeProperty(themeName, pressedId+"#marUnit", el.getThemeMarginUnit(pressedStyles));
currToken = "disabled margin";
res.setThemeProperty(themeName, disabledId+"#margin", el.getThemeMargin(disabledStyles));
currToken = "disabled marUnit";
res.setThemeProperty(themeName, disabledId+"#marUnit", el.getThemeMarginUnit(disabledStyles));
currToken = "elevation";
if (unselectedStyles.containsKey("elevation")) {
res.setThemeProperty(themeName, unselId + ".elevation", el.getThemeElevation(unselectedStyles));
}
currToken = "selected elevation";
if (selectedStyles.containsKey("elevation")) {
res.setThemeProperty(themeName, selId + "#elevation", el.getThemeElevation(selectedStyles));
}
currToken = "pressed elevation";
if (pressedStyles.containsKey("elevation")) {
res.setThemeProperty(themeName, pressedId + "#elevation", el.getThemeElevation(pressedStyles));
}
currToken = "disabled elevation";
if (disabledStyles.containsKey("elevation")) {
res.setThemeProperty(themeName, disabledId + "#elevation", el.getThemeElevation(disabledStyles));
}
currToken = "iconGap";
float gap = el.getThemeIconGap(unselectedStyles);
if (gap < 0) {
res.setThemeProperty(themeName, unselId+".iconGap", null);
currToken = "selected iconGap";
res.setThemeProperty(themeName, selId+"#iconGap", null);
currToken = "pressed iconGap";
res.setThemeProperty(themeName, pressedId+"#iconGap", null);
currToken = "disabled iconGap";
res.setThemeProperty(themeName, disabledId+"#iconGap", null);
currToken = "iconGapUnit";
res.setThemeProperty(themeName, unselId+".iconGapUnit", null);
currToken = "selected iconGapUnit";
res.setThemeProperty(themeName, selId+"#iconGapUnit", null);
currToken = "pressed iconGapUnit";
res.setThemeProperty(themeName, pressedId+"#iconGapUnit", null);
currToken = "disabled iconGapUnit";
res.setThemeProperty(themeName, disabledId+"#iconGapUnit", null);
} else {
res.setThemeProperty(themeName, unselId+".iconGap", gap);
currToken = "selected iconGap";
res.setThemeProperty(themeName, selId+"#iconGap", gap);
currToken = "pressed iconGap";
res.setThemeProperty(themeName, pressedId+"#iconGap", gap);
currToken = "disabled iconGap";
res.setThemeProperty(themeName, disabledId+"#iconGap", gap);
currToken = "iconGapUnit";
byte gapUnit = el.getThemeIconGapUnit(unselectedStyles);
res.setThemeProperty(themeName, unselId+".iconGapUnit", gapUnit);
currToken = "selected iconGapUnit";
res.setThemeProperty(themeName, selId+"#iconGapUnit", gapUnit);
currToken = "pressed iconGapUnit";
res.setThemeProperty(themeName, pressedId+"#iconGapUnit", gapUnit);
currToken = "disabled iconGapUnit";
res.setThemeProperty(themeName, disabledId+"#iconGapUnit", gapUnit);
}
currToken = "surface";
if (unselectedStyles.containsKey("surface")) {
res.setThemeProperty(themeName, unselId + ".surface", el.getThemeSurface(unselectedStyles));
}
currToken = "selected surface";
if (selectedStyles.containsKey("surface")) {
res.setThemeProperty(themeName, selId + "#surface", el.getThemeSurface(selectedStyles));
}
currToken = "pressed surface";
if (pressedStyles.containsKey("surface")) {
res.setThemeProperty(themeName, pressedId + "#surface", el.getThemeSurface(pressedStyles));
}
currToken = "disabled surface";
if (disabledStyles.containsKey("surface")) {
res.setThemeProperty(themeName, disabledId + "#surface", el.getThemeSurface(disabledStyles));
}
currToken = "fgColor";
res.setThemeProperty(themeName, unselId+".fgColor", el.getThemeFgColor(unselectedStyles));
currToken = "selected fgColor";
res.setThemeProperty(themeName, selId+"#fgColor", el.getThemeFgColor(selectedStyles));
currToken = "pressed fgColor";
res.setThemeProperty(themeName, pressedId+"#fgColor", el.getThemeFgColor(pressedStyles));
currToken = "disabled fgColor";
res.setThemeProperty(themeName, disabledId+"#fgColor", el.getThemeFgColor(disabledStyles));
currToken = "fgAlpha";
res.setThemeProperty(themeName, unselId+".fgAlpha", el.getThemeFgAlpha(unselectedStyles));
currToken = "selected fgAlpha";
res.setThemeProperty(themeName, selId+"#fgAlpha", el.getThemeFgAlpha(selectedStyles));
currToken = "pressed fgAlpha";
res.setThemeProperty(themeName, pressedId+"#fgAlpha", el.getThemeFgAlpha(pressedStyles));
currToken = "disabled fgAlpha";
res.setThemeProperty(themeName, disabledId+"#fgAlpha", el.getThemeFgAlpha(disabledStyles));
currToken = "bgColor";
res.setThemeProperty(themeName, unselId+".bgColor", el.getThemeBgColor(unselectedStyles));
currToken = "selected bgColor";
res.setThemeProperty(themeName, selId+"#bgColor", el.getThemeBgColor(selectedStyles));
currToken = "pressed bgColor";
res.setThemeProperty(themeName, pressedId+"#bgColor", el.getThemeBgColor(pressedStyles));
currToken = "disabled bgColor";
res.setThemeProperty(themeName, disabledId+"#bgColor", el.getThemeBgColor(disabledStyles));
currToken = "transparency";
res.setThemeProperty(themeName, unselId+".transparency", el.getThemeTransparency(unselectedStyles));
currToken = "selected transparency";
res.setThemeProperty(themeName, selId+"#transparency", el.getThemeTransparency(selectedStyles));
currToken = "pressed transparency";
res.setThemeProperty(themeName, pressedId+"#transparency", el.getThemeTransparency(pressedStyles));
currToken = "disabled transparency";
res.setThemeProperty(themeName, disabledId+"#transparency", el.getThemeTransparency(disabledStyles));
currToken = "align";
res.setThemeProperty(themeName, unselId+".align", el.getThemeAlignment(unselectedStyles));
currToken = "selected align";
res.setThemeProperty(themeName, selId+"#align", el.getThemeAlignment(selectedStyles));
currToken = "pressed align";
res.setThemeProperty(themeName, pressedId+"#align", el.getThemeAlignment(pressedStyles));
currToken = "disabled align";
res.setThemeProperty(themeName, disabledId+"#align", el.getThemeAlignment(disabledStyles));
currToken = "font";
res.setThemeProperty(themeName, unselId+".font", el.getThemeFont(unselectedStyles));
currToken = "selected font";
res.setThemeProperty(themeName, selId+"#font", el.getThemeFont(selectedStyles));
currToken = "pressed font";
res.setThemeProperty(themeName, pressedId+"#font", el.getThemeFont(pressedStyles));
currToken = "disabled font";
res.setThemeProperty(themeName, disabledId+"#font", el.getThemeFont(disabledStyles));
currToken = "textDecoration";
res.setThemeProperty(themeName, unselId+".textDecoration", el.getThemeTextDecoration(unselectedStyles));
currToken = "selected textDecoration";
res.setThemeProperty(themeName, selId+"#textDecoration", el.getThemeTextDecoration(selectedStyles));
currToken = "pressed textDecoration";
res.setThemeProperty(themeName, pressedId+"#textDecoration", el.getThemeTextDecoration(pressedStyles));
currToken = "disabled textDecoration";
res.setThemeProperty(themeName, disabledId+"#textDecoration", el.getThemeTextDecoration(disabledStyles));
currToken = "bgGradient";
res.setThemeProperty(themeName, unselId+".bgGradient", el.getThemeBgGradient(unselectedStyles));
currToken = "selected bgGradient";
res.setThemeProperty(themeName, selId+"#bgGradient", el.getThemeBgGradient(selectedStyles));
currToken = "pressed bgGradient";
res.setThemeProperty(themeName, pressedId+"#bgGradient", el.getThemeBgGradient(pressedStyles));
currToken = "disabled bgGradient";
res.setThemeProperty(themeName, disabledId+"#bgGradient", el.getThemeBgGradient(disabledStyles));
currToken = "bgType";
res.setThemeProperty(themeName, unselId+".bgType", el.getThemeBgType(unselectedStyles));
currToken = "selected bgType";
res.setThemeProperty(themeName, selId+"#bgType", el.getThemeBgType(selectedStyles));
currToken = "pressed bgType";
res.setThemeProperty(themeName, pressedId+"#bgType", el.getThemeBgType(pressedStyles));
currToken = "disabled bgType";
res.setThemeProperty(themeName, disabledId+"#bgType", el.getThemeBgType(disabledStyles));
currToken = "derive";
res.setThemeProperty(themeName, unselId+".derive", el.getThemeDerive(unselectedStyles, ""));
currToken = "selected derive";
res.setThemeProperty(themeName, selId+"#derive", el.getThemeDerive(selectedStyles, ".sel"));
currToken = "pressed derive";
res.setThemeProperty(themeName, pressedId+"#derive", el.getThemeDerive(pressedStyles, ".press"));
currToken = "disabled derive";
res.setThemeProperty(themeName, disabledId+"#derive", el.getThemeDerive(disabledStyles, ".dis"));
currToken = "opacity";
res.setThemeProperty(themeName, unselId+".opacity", el.getThemeOpacity(unselectedStyles));
currToken = "selected opacity";
res.setThemeProperty(themeName, selId+"#opacity", el.getThemeOpacity(selectedStyles));
currToken = "pressed opacity";
res.setThemeProperty(themeName, pressedId+"#opacity", el.getThemeOpacity(pressedStyles));
currToken = "disabled opacity";
res.setThemeProperty(themeName, disabledId+"#opacity", el.getThemeOpacity(disabledStyles));
currToken = "bgImage";
if (el.hasBackgroundImage(unselectedStyles) && !el.requiresBackgroundImageGeneration(unselectedStyles) && !el.requiresImageBorder(unselectedStyles)) {
Image[] imageId = getBackgroundImages(unselectedStyles);
if (imageId != null && imageId.length > 0) {
res.setThemeProperty(themeName, unselId+".bgImage", imageId[0]);
}
}
currToken = "selected bgImage";
if (el.hasBackgroundImage(selectedStyles) && !el.requiresBackgroundImageGeneration(selectedStyles) && !el.requiresImageBorder(selectedStyles)) {
Image[] imageId = getBackgroundImages(selectedStyles);
if (imageId != null && imageId.length > 0) {
res.setThemeProperty(themeName, selId+"#bgImage", imageId[0]);
}
}
currToken = "pressed bgImage";
if (el.hasBackgroundImage(pressedStyles) && !el.requiresBackgroundImageGeneration(pressedStyles) && !el.requiresImageBorder(pressedStyles)) {
Image[] imageId = getBackgroundImages(pressedStyles);
if (imageId != null && imageId.length > 0) {
res.setThemeProperty(themeName, pressedId+"#bgImage", imageId[0]);
}
}
currToken = "disabled bgImage";
if (el.hasBackgroundImage(disabledStyles) && !el.requiresBackgroundImageGeneration(disabledStyles) && !el.requiresImageBorder(disabledStyles)) {
Image[] imageId = getBackgroundImages(disabledStyles);
if (imageId != null && imageId.length > 0) {
res.setThemeProperty(themeName, disabledId+"#bgImage", imageId[0]);
}
}
currToken = "border";
if (!el.requiresImageBorder(unselectedStyles) && !el.requiresBackgroundImageGeneration(unselectedStyles)) {
res.setThemeProperty(themeName, unselId+".border", el.getThemeBorder(unselectedStyles));
}
currToken = "selected border";
if (!el.requiresImageBorder(selectedStyles) && !el.requiresBackgroundImageGeneration(selectedStyles)) {
res.setThemeProperty(themeName, selId+"#border", el.getThemeBorder(selectedStyles));
}
currToken = "pressed border";
if (!el.requiresImageBorder(pressedStyles) && !el.requiresBackgroundImageGeneration(pressedStyles)) {
res.setThemeProperty(themeName, pressedId+"#border", el.getThemeBorder(pressedStyles));
}
currToken = "disabled border";
if (!el.requiresImageBorder(disabledStyles) && !el.requiresBackgroundImageGeneration(disabledStyles)) {
res.setThemeProperty(themeName, disabledId+"#border", el.getThemeBorder(disabledStyles));
}
} catch (RuntimeException t) {
System.err.println("An error occurred while updating resources for UIID "+id+". Processing property "+currToken);
throw t;
}
}
res.setThemeProperty(themeName, "@PopupDialogArrowBool", "false");
for (String constantKey : constants.keySet()) {
try {
LexicalUnit lu = constants.get(constantKey);
if (lu.getLexicalUnitType() == LexicalUnit.SAC_STRING_VALUE || lu.getLexicalUnitType() == LexicalUnit.SAC_IDENT) {
if (constantKey.endsWith("Image")) {
// We have an image
Image im = res.getImage(lu.getStringValue());
if (im == null) {
im = getResourceImage(lu.getStringValue());
}
if (im == null) {
System.err.println("Error processing file "+this.baseURL);
throw new RuntimeException("Failed to set constant value "+constantKey+" to value "+ lu.getStringValue()+" because no such image was found in the resource file");
}
res.setThemeProperty(themeName, "@"+constantKey, im);
} else {
res.setThemeProperty(themeName, "@"+constantKey, lu.getStringValue());
}
} else if (lu.getLexicalUnitType() == LexicalUnit.SAC_INTEGER) {
res.setThemeProperty(themeName, "@"+constantKey, String.valueOf(((ScaledUnit)lu).getIntegerValue()));
}
} catch (RuntimeException t) {
System.err.println("\nAn error occurred processing constant key "+constantKey);
throw t;
}
}
imagesMetadata.store(res);
Map theme = res.getTheme(themeName);
HashSet keys = new HashSet();
keys.addAll(theme.keySet());
Set deletedIds = getDeletedElements();
for (String key : keys) {
if (key.startsWith("Default.")) {
res.setThemeProperty(themeName, key.substring(key.indexOf(".")+1), theme.get(key));
} else {
for (String delId : deletedIds) {
if (key.startsWith(delId+".") || key.startsWith(delId+"#")) {
res.setThemeProperty(themeName, key, null);
}
}
}
}
// Get rid of unused images now
deleteUnusedImages();
}
private Set referencedImageNames;
private boolean isImageReferencedInCSS(String imageName) {
if (referencedImageNames == null) {
referencedImageNames = new HashSet();
for (String id : elements.keySet()) {
Element el = elements.get(id);
for (Object o : el.style.values()) {
ScaledUnit su = (ScaledUnit)o;
while (su != null) {
switch (su.getLexicalUnitType()) {
case LexicalUnit.SAC_STRING_VALUE:
referencedImageNames.add(su.getStringValue());
break;
case LexicalUnit.SAC_URI: {
String sv = su.getStringValue();
if (sv.contains("/")) {
sv = sv.substring(sv.lastIndexOf("/")+1);
}
referencedImageNames.add(sv);
break;
}
}
su = (ScaledUnit)su.getNextLexicalUnit();
}
}
}
}
return referencedImageNames.contains(imageName);
}
public void deleteUnusedImages() {
Vector images = new Vector();
for(String img : res.getImageResourceNames()) {
if(!isInUse(img)) {
images.add(img);
}
}
for (String im : images) {
res.remove(im);
}
}
private boolean isInUse(String imageName) {
if (isImageReferencedInCSS(imageName)) {
return true;
}
Object multi = res.getResourceObject(imageName);
if(multi instanceof EditableResources.MultiImage) {
EditableResources.MultiImage m = (EditableResources.MultiImage)multi;
for(com.codename1.ui.Image i : m.getInternalImages()) {
if(isInUse(i)) {
return true;
}
}
return false;
}
com.codename1.ui.Image resourceValue = res.getImage(imageName);
return isInUse(resourceValue);
}
private boolean isInUse(com.codename1.ui.Image resourceValue) {
for(String themeName : res.getThemeResourceNames()) {
Hashtable theme = res.getTheme(themeName);
if(theme.values().contains(resourceValue)) {
return true;
}
// we need to check the existance of image borders to replace images there...
for(Object v : theme.values()) {
if(v instanceof com.codename1.ui.plaf.Border) {
com.codename1.ui.plaf.Border b = (com.codename1.ui.plaf.Border)v;
// BORDER_TYPE_IMAGE
if(Accessor.getType(b) == Accessor.TYPE_IMAGE || Accessor.getType(b) == Accessor.TYPE_IMAGE_HORIZONTAL ||
Accessor.getType(b) == Accessor.TYPE_IMAGE_VERTICAL || Accessor.getType(b) == Accessor.TYPE_IMAGE_SCALED) {
com.codename1.ui.Image[] images = Accessor.getImages(b);
for(int i = 0 ; i < images.length ; i++) {
if(images[i] == resourceValue) {
return true;
}
}
}
}
}
}
// check if a timeline is making use of said image and replace it
for(String image : res.getImageResourceNames()) {
com.codename1.ui.Image current = res.getImage(image);
if(current instanceof com.codename1.ui.animations.Timeline) {
com.codename1.ui.animations.Timeline time = (com.codename1.ui.animations.Timeline)current;
for(int iter = 0 ; iter < time.getAnimationCount() ; iter++) {
com.codename1.ui.animations.AnimationObject o = time.getAnimation(iter);
if(AnimationAccessor.getImage(o) == resourceValue) {
return true;
}
}
}
}
return false;
}
private Map loadedImages = new LinkedHashMap();
public int[] getDpi(EncodedImage im) throws IOException {
ImageInputStream iis = ImageIO.createImageInputStream(new ByteArrayInputStream(im.getImageData()));
Iterator it = ImageIO.getImageReaders(iis);
if (!it.hasNext()) {
System.err.println("No reader for this format");
return null;
}
ImageReader reader = (ImageReader) it.next();
reader.setInput(iis);
IIOMetadata meta = reader.getImageMetadata(0);
IIOMetadataNode root = (IIOMetadataNode) meta.getAsTree("javax_imageio_1.0");
NodeList nodes = root.getElementsByTagName("HorizontalPixelSize");
int xDPI = -1;
int yDPI = -1;
if (nodes.getLength() > 0) {
IIOMetadataNode dpcWidth = (IIOMetadataNode) nodes.item(0);
NamedNodeMap nnm = dpcWidth.getAttributes();
Node item = nnm.item(0);
xDPI = Math.round(25.4f / Float.parseFloat(item.getNodeValue()) / 0.45f);
} else {
}
if (nodes.getLength() > 0) {
nodes = root.getElementsByTagName("VerticalPixelSize");
IIOMetadataNode dpcHeight = (IIOMetadataNode) nodes.item(0);
NamedNodeMap nnm = dpcHeight.getAttributes();
Node item = nnm.item(0);
yDPI = Math.round(25.4f / Float.parseFloat(item.getNodeValue()) / 0.45f);
}
return new int[]{xDPI, yDPI};
}
public int getDensityForDpi(double maxDpi) {
if (maxDpi <= 120) {
return com.codename1.ui.Display.DENSITY_LOW;
}
if (maxDpi <= 160) {
return com.codename1.ui.Display.DENSITY_MEDIUM;
} else if (maxDpi <= 320) {
return com.codename1.ui.Display.DENSITY_VERY_HIGH;
} else if (maxDpi <= 200) {
return com.codename1.ui.Display.DENSITY_560;
} else if (maxDpi <= 480) {
return com.codename1.ui.Display.DENSITY_HD;
} else {
return com.codename1.ui.Display.DENSITY_2HD;
}
}
public int getImageDensity(EncodedImage im) {
try {
int[] dpis = getDpi(im);
int maxDpi = 160;
if (dpis != null && dpis[0] > 0 && dpis[0] > maxDpi) {
maxDpi = dpis[0];
}
if (dpis != null && dpis[1] > 0 && dpis[1] > maxDpi) {
maxDpi = dpis[1];
}
return getDensityForDpi(maxDpi);
} catch (Exception ex) {
return com.codename1.ui.Display.DENSITY_MEDIUM;
}
}
public Image[] getBackgroundImages(Map styles) {
ScaledUnit bgImage = (ScaledUnit)styles.get("background-image");
List out = new ArrayList();
while (bgImage != null) {
Image im = getBackgroundImage(styles, bgImage);
if (im != null) {
out.add(im);
}
bgImage = (ScaledUnit)bgImage.getNextLexicalUnit();
while (bgImage != null && bgImage.getLexicalUnitType() != LexicalUnit.SAC_URI) {
bgImage = (ScaledUnit)bgImage.getNextLexicalUnit();
}
}
return out.toArray(new Image[out.size()]);
}
public Image getBackgroundImage(Map styles) {
return getBackgroundImage(styles, (ScaledUnit)styles.get("background-image"));
}
public Image getResourceImage(String imageName) {
if (new File(cssFile, "res").exists()) {
File res = new File(cssFile, "res");
File preferredThemeDir = new File(res, themeName);
if (preferredThemeDir.exists()) {
Image i = null;
try {
i = getResourceImage(imageName, preferredThemeDir);
if (i != null) {
return i;
}
} catch (Throwable t) {
t.printStackTrace();
}
}
for (File d : res.listFiles()) {
if (d.isDirectory()) {
Image i = null;
try {
i = getResourceImage(imageName, d);
if (i!=null) {
return i;
}
} catch (Throwable t){
t.printStackTrace();
}
}
}
}
if (new File(cssFile.getParentFile(), "res").exists()) {
File res = new File(cssFile.getParentFile(), "res");
File preferredThemeDir = new File(res, themeName);
if (preferredThemeDir.exists()) {
Image i = null;
try {
i = getResourceImage(imageName, preferredThemeDir);
if (i != null) {
return i;
}
} catch (Throwable t) {
t.printStackTrace();
}
}
for (File d : res.listFiles()) {
if (d.isDirectory()) {
Image i = null;
try {
i = getResourceImage(imageName, d);
if (i!=null) {
return i;
}
} catch (Throwable t){
t.printStackTrace();
}
}
}
}
if (new File(cssFile.getParentFile().getParentFile(), "res").exists()) {
File res = new File(cssFile.getParentFile().getParentFile(), "res");
File preferredThemeDir = new File(res, themeName);
if (preferredThemeDir.exists()) {
Image i = null;
try {
i = getResourceImage(imageName, preferredThemeDir);
if (i != null) {
return i;
}
} catch (Throwable t) {
t.printStackTrace();
}
}
for (File d : res.listFiles()) {
if (d.isDirectory()) {
Image i = null;
try {
i = getResourceImage(imageName, d);
if (i!=null) {
return i;
}
} catch (Throwable t){
t.printStackTrace();
}
}
}
}
return null;
}
public Image getResourceImage(String imageName, File baseDir) {
try {
if (res.containsResource(imageName)) {
return res.getImage(imageName);
} else {
File imageFolder = new File(baseDir, imageName );
if (imageFolder.exists()) {
EditableResources.MultiImage multi = new EditableResources.MultiImage();
ArrayList dpis = new ArrayList();
ArrayList encodedImages = new ArrayList();
//File largestVersion = null;
//long largestSize = 0;
for (File f : imageFolder.listFiles()) {
int density = Display.DENSITY_MEDIUM;
switch (f.getName()) {
case "2hd.png":
density = Display.DENSITY_2HD; break;
case "4k.png":
density = Display.DENSITY_4K; break;
case "560.png":
density = Display.DENSITY_560; break;
case "hd.png":
density = Display.DENSITY_HD;break;
case "high.png":
density = Display.DENSITY_HIGH; break;
case "low.png":
density = Display.DENSITY_LOW; break;
case "medium.png":
density = Display.DENSITY_MEDIUM; break;
case "veryhigh.png":
density = Display.DENSITY_VERY_HIGH; break;
case "verylow.png":
density = Display.DENSITY_VERY_LOW; break;
}
InputStream is = f.toURL().openStream();
EncodedImage encImg = EncodedImage.create(is);
is.close();
dpis.add(density);
encodedImages.add(encImg);
}
int[] iDpis = new int[dpis.size()];
int ctr = 0;
for (Integer i : dpis) {
iDpis[ctr++] = i;
}
multi.setDpi(iDpis);
multi.setInternalImages(encodedImages.toArray(new EncodedImage[encodedImages.size()]));
res.setMultiImage(imageName, multi);
loadedImages.put(imageFolder.toURL().toString(), multi.getBest());
return multi.getBest();
}
}
} catch (Exception ex) {
throw new RuntimeException(ex);
}
return null;
}
private boolean isFileURL(URL url) {
return "file".equals(url.getProtocol()) && (url.getHost() == null || "".equals(url.getHost()));
}
public Image getBorderImage(Map styles) {
return getBackgroundImage(styles, (ScaledUnit)styles.get("border-image"));
}
public Image getBackgroundImage(Map styles, ScaledUnit bgImage) {
try {
//ScaledUnit bgImage = (ScaledUnit)styles.get("background-image");
if (bgImage == null) {
return null;
}
String url = bgImage.getStringValue();
String fileName = url;
if (fileName.indexOf("/") != -1) {
fileName = fileName.substring(fileName.lastIndexOf("/")+1);
}
if (loadedImages.containsKey(url)) {
return loadedImages.get(url);
}
LexicalUnit imageId = styles.get("cn1-image-id");
String imageIdStr = fileName;
if (imageId != null) {
imageIdStr = imageId.getStringValue();
} else {
/*
int i=1;
while (res.getImage(imageIdStr) != null) {
if (i == 1) {
imageIdStr += "_"+(++i);
} else {
imageIdStr = imageIdStr.substring(0, imageIdStr.lastIndexOf("_")) + "_"+(++i);
}
}
*/
}
Image resimg = res.getImage(imageIdStr);
Integer defaultSourceDpi = null;
if (constants.containsKey("defaultSourceDPIInt")) {
Object v = constants.get("defaultSourceDPIInt");
if (v instanceof String) {
defaultSourceDpi = Integer.parseInt((String)v);
} else if (v instanceof Number) {
defaultSourceDpi = ((Number) v).intValue();
} else if (v instanceof ScaledUnit) {
ScaledUnit su = (ScaledUnit)v;
defaultSourceDpi = su.getIntegerValue();
} else {
throw new IllegalArgumentException("defaultSourceDPIInt constant should be a String or a number but found "+v.getClass());
}
}
if (resimg != null) {
ImageMetadata md = imagesMetadata.get(imageIdStr);
int srcDpi = (int)currentDpi;
if (defaultSourceDpi != null) {
srcDpi = defaultSourceDpi;
}
if (styles.containsKey("cn1-source-dpi")) {
srcDpi = (int)((ScaledUnit)styles.get("cn1-source-dpi")).getNumericValue();
}
if (refreshImages || (md != null && md.sourceDpi != srcDpi)) {
//
res.remove(imageIdStr);
} else {
loadedImages.put(imageIdStr, resimg);
return res.getImage(imageIdStr);
}
}
URL imgURL = null;
if (url.startsWith("http://") || url.startsWith("https://")) {
imgURL = new URL(url);
} else {
imgURL = new URL(baseURL, url);
}
if (false && isFileURL(imgURL)) {
// This section is switched off because loading multi-images via url()
// will cause unexpected results in cases where image borders are generated.
// In order for this approach to work, we need take into account multi-images when
// producing snapshots in the webview so that the correct size of image is used.
// You can still load multi-images as theme constants.
// See https://github.com/codenameone/CodenameOne/issues/2569#issuecomment-426730539
File imgDir = new File(imgURL.toURI());
if (imgDir.isDirectory()) {
try {
Image im = getResourceImage(imgDir.getName(), imgDir.getParentFile());
if (im != null) {
loadedImages.put(url, im);
return im;
}
} catch (Throwable t) {
System.err.println("Failed to load Multi-image from "+imgURL);
t.printStackTrace();
throw t;
}
}
}
InputStream is = imgURL.openStream();
EncodedImage encImg = EncodedImage.create(is);
is.close();
ResourcesMutator resm = new ResourcesMutator(res, com.codename1.ui.Display.DENSITY_VERY_HIGH, minDpi, maxDpi);
int[] dpis = getDpi(encImg);
int sourceDpi = (int)Math.round(currentDpi);
if (styles.containsKey("cn1-source-dpi")) {
double densityVal = ((ScaledUnit) styles.get("cn1-source-dpi")).getNumericValue();
sourceDpi = (int) Math.round(densityVal);
if (Math.abs(densityVal) < 0.5) {
resm.targetDensity = 0;
} else {
resm.targetDensity = getDensityForDpi(densityVal);
}
} else if (defaultSourceDpi != null) {
sourceDpi = defaultSourceDpi;
if (Math.abs(sourceDpi) < 0.5) {
resm.targetDensity = 0;
} else {
resm.targetDensity = getDensityForDpi(sourceDpi);
}
} else if (dpis[0] > 0) {
resm.targetDensity = getImageDensity(encImg);
} else {
resm.targetDensity = getDensityForDpi(bgImage.dpi);
}
if (styles.containsKey("cn1-densities")) {
ScaledUnit densities = (ScaledUnit)styles.get("cn1-densities");
if (densities.getLexicalUnitType() == LexicalUnit.SAC_IDENT && "none".equals(densities.getStringValue())) {
// Not a multi-image
resm.setMultiImage(false);
}
} else if (sourceDpi == 0) {
resm.setMultiImage(false);
}
Image im = resm.storeImage(encImg, imageIdStr, false);
im.setImageName(imageIdStr);
loadedImages.put(url, im);
ImageMetadata md = new ImageMetadata(imageIdStr, sourceDpi);
imagesMetadata.addImageMetadata(md);
return im;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public int getSourceDensity(Map style, int defaultValue) {
if (style.containsKey("cn1-source-dpi")) {
return getDensityForDpi(((ScaledUnit)style.get("cn1-source-dpi")).getNumericValue());
} else {
return defaultValue;
}
}
public void loadResourceFile() throws IOException {
if ( resourceFile != null && resourceFile.exists()) {
if (res == null) {
res = new EditableResourcesForCSS(resourceFile);
}
try {
res.openFile(new FileInputStream(resourceFile));
} catch (IOException ex) {
System.err.println("Failed to load resource file from "+resourceFile);
throw ex;
}
imagesMetadata.load(res);
}
}
public static interface WebViewProvider {
com.codename1.ui.BrowserComponent getWebView();
}
private static String currentId;
public void createImageBorders(WebViewProvider webviewProvider) {
if (res == null) {
res = new EditableResourcesForCSS(resourceFile);
}
ArrayList borders = new ArrayList();
ResourcesMutator resm = new ResourcesMutator(res, Display.DENSITY_VERY_HIGH, minDpi, maxDpi);
resm.targetDensity = targetDensity;
List onComplete = new ArrayList();
for (String id : elements.keySet()) {
try {
if (!isModified(id)) {
continue;
}
Element e = (Element) elements.get(id);
Element unselected = e.getUnselected();
Map unselectedStyles = (Map) unselected.getFlattenedStyle();
Border b = unselected.createBorder(unselectedStyles);
Border unselectedBorder = b;
currentId = id;
if (e.requiresImageBorder(unselectedStyles)) {
if (!borders.contains(b)) {
borders.add(b);
resm.addImageProcessor(id, (img) -> {
Insets insets = unselected.getImageBorderInsets(unselectedStyles, img.getWidth(), img.getHeight());
resm.targetDensity = getSourceDensity(unselectedStyles, resm.targetDensity);
com.codename1.ui.plaf.Border border = resm.create9PieceBorder(img, id, (int) insets.top, (int) insets.right, (int) insets.bottom, (int) insets.left);
resm.put(id + ".border", border);
unselectedBorder.border = border;
resm.targetDensity = targetDensity;
});
} else {
onComplete.add(() -> {
resm.put(id + ".border", borders.get(borders.indexOf(unselectedBorder)).border);
});
}
} else if (e.requiresBackgroundImageGeneration(unselectedStyles)) {
if (!borders.contains(b)) {
borders.add(b);
resm.addImageProcessor(id, (img) -> {
int i = 1;
while (res.containsResource(id + "_" + i + ".png")) {
i++;
}
String prefix = id + "_" + i + ".png";
resm.targetDensity = getSourceDensity(unselectedStyles, resm.targetDensity);
Image im = resm.storeImage(EncodedImage.create(ResourcesMutator.toPngOrJpeg(img)), prefix, false);
unselectedBorder.image = im;
resm.put(id + ".bgImage", im);
resm.targetDensity = targetDensity;
//resm.put(id+".press#bgType", Style.B)
});
} else {
onComplete.add(() -> {
resm.put(id + ".bgImage", unselectedBorder.image);
});
}
}
Element selected = e.getSelected();
Map selectedStyles = (Map) selected.getFlattenedStyle();
b = selected.createBorder(selectedStyles);
Border selectedBorder = b;
if (e.requiresImageBorder(selectedStyles)) {
if (!borders.contains(b)) {
borders.add(b);
resm.addImageProcessor(id + ".sel", (img) -> {
Insets insets = selected.getImageBorderInsets(selectedStyles, img.getWidth(), img.getHeight());
resm.targetDensity = getSourceDensity(selectedStyles, resm.targetDensity);
com.codename1.ui.plaf.Border border = resm.create9PieceBorder(img, id, (int) insets.top, (int) insets.right, (int) insets.bottom, (int) insets.left);
resm.put(id + ".sel#border", border);
selectedBorder.border = border;
resm.targetDensity = targetDensity;
});
} else {
onComplete.add(() -> {
resm.put(id + ".sel#border", borders.get(borders.indexOf(selectedBorder)).border);
});
}
} else if (e.requiresBackgroundImageGeneration(selectedStyles)) {
if (!borders.contains(b)) {
borders.add(b);
resm.addImageProcessor(id + ".sel", (img) -> {
int i = 1;
while (res.containsResource(id + "_" + i + ".png")) {
i++;
}
String prefix = id + "_" + i + ".png";
resm.targetDensity = getSourceDensity(selectedStyles, resm.targetDensity);
Image im = resm.storeImage(EncodedImage.create(ResourcesMutator.toPngOrJpeg(img)), prefix, false);
selectedBorder.image = im;
resm.put(id + ".sel#bgImage", im);
//resm.put(id+".press#bgType", Style.B)
resm.targetDensity = targetDensity;
});
} else {
onComplete.add(() -> {
resm.put(id + ".sel#bgImage", selectedBorder.image);
});
}
}
Element pressed = e.getPressed();
Map pressedStyles = (Map) pressed.getFlattenedStyle();
b = pressed.createBorder(pressedStyles);
Border pressedBorder = b;
if (e.requiresImageBorder(pressedStyles)) {
if (!borders.contains(b)) {
borders.add(b);
resm.addImageProcessor(id + ".press", (img) -> {
Insets insets = pressed.getImageBorderInsets(pressedStyles, img.getWidth(), img.getHeight());
resm.targetDensity = getSourceDensity(pressedStyles, resm.targetDensity);
com.codename1.ui.plaf.Border border = resm.create9PieceBorder(img, id, (int) insets.top, (int) insets.right, (int) insets.bottom, (int) insets.left);
resm.put(id + ".press#border", border);
pressedBorder.border = border;
resm.targetDensity = targetDensity;
});
} else {
onComplete.add(() -> {
resm.put(id + ".press#border", borders.get(borders.indexOf(pressedBorder)).border);
});
}
} else if (e.requiresBackgroundImageGeneration(pressedStyles)) {
if (!borders.contains(b)) {
borders.add(b);
resm.addImageProcessor(id + ".press", (img) -> {
int i = 1;
while (res.containsResource(id + "_" + i + ".png")) {
i++;
}
String prefix = id + "_" + i + ".png";
resm.targetDensity = getSourceDensity(pressedStyles, resm.targetDensity);
Image im = resm.storeImage(EncodedImage.create(ResourcesMutator.toPngOrJpeg(img)), prefix, false);
pressedBorder.imageId = prefix;
resm.put(id + ".press#bgImage", im/*res.findId(im, true)*/);
resm.targetDensity = targetDensity;
//resm.put(id+".press#bgType", Style.B)
});
} else {
onComplete.add(() -> {
resm.put(id + ".press#bgImage", res.findId(pressedBorder.imageId, true));
});
}
}
Element disabled = e.getDisabled();
Map disabledStyles = (Map) disabled.getFlattenedStyle();
b = disabled.createBorder(disabledStyles);
Border disabledBorder = b;
if (e.requiresImageBorder(disabledStyles)) {
if (!borders.contains(b)) {
borders.add(b);
resm.addImageProcessor(id + ".dis", (img) -> {
Insets disabledInsets = disabled.getImageBorderInsets(disabledStyles, img.getWidth(), img.getHeight());
resm.targetDensity = getSourceDensity(disabledStyles, resm.targetDensity);
com.codename1.ui.plaf.Border border = resm.create9PieceBorder(img, id, (int) disabledInsets.top, (int) disabledInsets.right, (int) disabledInsets.bottom, (int) disabledInsets.left);
disabledBorder.border = border;
resm.put(id + ".dis#border", border);
resm.targetDensity = targetDensity;
});
} else {
onComplete.add(() -> {
resm.put(id + ".dis#border", borders.get(borders.indexOf(disabledBorder)).border);
});
}
} else if (e.requiresBackgroundImageGeneration(disabledStyles)) {
if (!borders.contains(b)) {
borders.add(b);
resm.addImageProcessor(id + ".dis", (img) -> {
int i = 1;
while (res.containsResource(id + "_" + i + ".png")) {
i++;
}
String prefix = id + "_" + i + ".png";
resm.targetDensity = getSourceDensity(disabledStyles, resm.targetDensity);
Image im = resm.storeImage(EncodedImage.create(ResourcesMutator.toPngOrJpeg(img)), prefix, false);
disabledBorder.image = im;
resm.put(id + ".dis#bgImage", im);
resm.targetDensity = targetDensity;
//resm.put(id+".press#bgType", Style.B)
});
} else {
onComplete.add(() -> {
resm.put(id + ".dis#bgImage", disabledBorder.image);
});
}
}
} catch (Exception ex) {
throw new RuntimeException("An exception occurred while processing the image border for element "+id, ex);
}
}
if (requiresCaptureHtml()) {
resm.createScreenshots(webviewProvider.getWebView(), generateCaptureHtml(), this.baseURL.toExternalForm());
}
for (Runnable r : onComplete) {
r.run();
}
}
public void save(File outputFile) throws IOException {
DataOutputStream resFile = new DataOutputStream(new FileOutputStream(outputFile));
res.save(resFile);
resFile.close();
}
private class Border {
com.codename1.ui.plaf.Border border;
String imageId;
Image image;
String thicknessTop,
thicknessRight,
thicknessBottom,
thicknessLeft,
styleTop,
styleRight,
styleBottom,
styleLeft;
String backgroundImageUrl,
backgroundRepeat,
borderRadius,
boxShadow,
gradient;
String backgroundColor,
borderColorTop,
borderColorRight,
borderColorBottom,
borderColorLeft;
String borderImage,
borderImageSlice;
boolean isStyleNativelySupported() {
return this.styleTop == null || isBorderTypeNativelySupported(this.styleTop);
}
boolean isBorderLineOrNone() {
return styleTop == null || "none".equals(styleTop) || "line".equals(styleTop) || "solid".equals(styleTop);
}
public boolean canBeAchievedWithUnderlineBorder(Map styles) {
if (this.hasGradient() || !isBorderLineOrNone() || !isNone(backgroundImageUrl) || hasBoxShadow() || hasBorderImage()) {
return false;
}
ScaledUnit topThickness = (ScaledUnit)styles.get("border-top-width");
ScaledUnit leftThickness = (ScaledUnit)styles.get("border-left-width");
ScaledUnit rightThickness = (ScaledUnit)styles.get("border-right-width");
ScaledUnit bottomThickness = (ScaledUnit)styles.get("border-bottom-width");
ScaledUnit[] sideUnits = new ScaledUnit[]{topThickness, leftThickness, rightThickness};
String[] sideStyles = new String[]{styleTop, styleLeft, styleRight};
for (int i=0; i<3; i++) {
ScaledUnit u = sideUnits[i];
String s = sideStyles[i];
if (u != null && u.getPixelValue() != 0 && !(s == null || "none".equals(s))) {
return false;
}
}
if (bottomThickness != null && bottomThickness.getPixelValue() != 0 && ("line".equals(styleBottom) || "solid".equals(styleBottom))) {
LexicalUnit color = styles.get("border-bottom-color");
if (color != null && getColorAlphaInt(color) != 255) {
return false;
}
return true;
}
return false;
}
/**
* Eventually we hope to support all borders with the CSSBorder class, but for now,
* we are introducing its usage gradually to just cover the common cases. One common
* case is where there is a bottom border.
* @param styles
* @return
*/
public boolean canBeAchievedWithCSSBorder(Map styles) {
if (this.hasGradient() || !isNone(backgroundImageUrl) || hasBoxShadow()) {
return false;
}
return true;
}
public boolean canBeAchievedWithRoundRectBorder(Map styles) {
if (hasUnequalBorders() || this.hasGradient() || !isBorderLineOrNone() || !isNone(backgroundImageUrl) || hasBoxShadow() || hasBorderImage()) {
return false;
}
String prefix = "cn1-border";
String[] corners = new String[]{"top-left", "top-right", "bottom-left", "bottom-right"};
String[] xy = new String[]{"x", "y"};
String[] radiusAtts = new String[8];
int i =0;
for (String axis : xy) {
for (String corner : corners) {
radiusAtts[i++] = prefix+"-"+corner+"-radius-"+axis;
}
}
ScaledUnit val = null;
for (String cornerStyle : radiusAtts) {
ScaledUnit u = (ScaledUnit)styles.get(cornerStyle);
if (u != null && u.getPixelValue() != 0) {
if (val != null && val.getPixelValue() != u.getPixelValue()) {
// We have more than one non-zero corner radius
return false;
}
val = u;
}
}
// All corners are the same, so we can proceed to the next step.
prefix = "border";
String[] sides = new String[]{"top", "right", "bottom", "left"};
String[] widthAtts = new String[4];
String[] colorAtts = new String[4];
i=0;
for (String side : sides) {
widthAtts[i] = "border-"+side+"-width";
colorAtts[i++] = "border-"+side+"-color";
}
boolean borderColorSet=false;
boolean borderWidthSet=false;
int borderWidth=0;
int colorInt=0;
int alphaInt=0;
for (String widthAtt : widthAtts) {
ScaledUnit uWidth = (ScaledUnit)styles.get(widthAtt);
if (uWidth != null) {
if (borderWidthSet && uWidth.getPixelValue() != borderWidth) {
return false;
}
borderWidthSet = true;
borderWidth = uWidth.getPixelValue();
}
}
for (String colorAtt : colorAtts) {
LexicalUnit uColor = styles.get(colorAtt);
if (uColor != null) {
if (borderColorSet && (getColorInt(uColor) != colorInt || getColorAlphaInt(uColor) != alphaInt)) {
return false;
}
borderColorSet = true;
colorInt = getColorInt(uColor);
alphaInt = getColorAlphaInt(uColor);
}
}
// We should be able to achieve this with a roundrect border
return true;
}
public boolean equals(Object o) {
Border b = (Border)o;
return eq(borderRadius, b.borderRadius)
&& eq(thicknessTop, b.thicknessTop)
&& eq(thicknessRight, b.thicknessRight)
&& eq(thicknessBottom, b.thicknessBottom)
&& eq(thicknessLeft, b.thicknessLeft)
&& eq(styleTop, b.styleTop)
&& eq(styleRight, b.styleRight)
&& eq(styleBottom, b.styleBottom)
&& eq(styleLeft, b.styleLeft)
&& eq(gradient, b.gradient)
&& eq(boxShadow, b.boxShadow)
&& (backgroundImageUrl == null ? b.backgroundImageUrl == null : backgroundImageUrl.equals(b.backgroundImageUrl))
&& (backgroundRepeat == null ? b.backgroundRepeat == null : backgroundRepeat.equals(b.backgroundRepeat))
&& eq(backgroundColor, b.backgroundColor)
&& eq(borderColorTop, b.borderColorTop)
&& eq(borderColorRight, b.borderColorRight)
&& eq(borderColorBottom, b.borderColorBottom)
&& eq(borderColorLeft, b.borderColorLeft)
&& eq(borderImage, b.borderImage)
&& eq(borderImageSlice, b.borderImageSlice);
}
/*
public boolean requiresImageBorder() {
return !eq(borderColorTop, borderColorRight)
|| !eq(borderColorTop, borderColorBottom)
|| !eq(borderColorTop, borderColorLeft)
|| !isNone(gradient)
|| !isNone(boxShadow)
|| !isZero(borderRadius)
|| !eq(thicknessTop, thicknessRight)
|| !eq(thicknessTop, thicknessBottom)
|| !eq(thicknessTop, thicknessLeft)
|| !eq(styleTop, styleRight)
|| !eq(styleTop, styleBottom)
|| !eq(styleTop, styleLeft);
}
*/
public String toString() {
return borderColorTop + " " +borderColorRight + borderColorBottom + borderColorLeft +
gradient + boxShadow + borderRadius + thicknessTop + thicknessRight + thicknessBottom + thicknessLeft
+ styleTop + styleRight + styleBottom + styleLeft + borderImage + borderImageSlice;
}
public boolean hasBorderRadius() {
String br = borderRadius;
if (br != null) {
if (br.indexOf(":") > 0) {
br = br.substring(br.indexOf(":")+1).trim();
}
}
if (br == null || br.isEmpty()) {
return false;
}
return !isZero(br);
}
public boolean hasGradient() {
return !isNone(gradient);
}
public boolean hasUnequalBorders() {
return !eq(thicknessTop, thicknessRight)
|| !eq(thicknessTop, thicknessBottom)
|| !eq(thicknessTop, thicknessLeft)
|| !eq(styleTop, styleRight)
|| !eq(styleTop, styleBottom)
|| !eq(styleTop, styleLeft)
|| !eq(borderColorTop, borderColorRight)
|| !eq(borderColorTop, borderColorBottom)
|| !eq(borderColorTop, borderColorLeft);
}
public boolean hasBoxShadow() {
String bs = boxShadow;
if (bs != null) {
if (bs.indexOf(":") > 0) {
bs = bs.substring(bs.indexOf(":")+1).trim();
}
}
return !isNone(bs);
}
public boolean hasBorderImage() {
String bs = borderImage;
if (bs != null) {
if (bs.indexOf(":") > 0) {
bs = bs.substring(bs.indexOf(":")+1).trim();
}
}
return !isNone(bs);
}
}
private static boolean eq(Object o1, Object o2) {
return o1 == null ? o1 == null : o1.equals(o2);
}
private static boolean isNone(Object o) {
return o == null || "".equals(o) || "none".equals(o);
}
private static boolean isZero(String o) {
if (o == null || "".equals(o)) {
return true;
}
o = o.replaceAll("[^0-9]", "");
return o.matches("^0*$");
}
private static boolean isZero(ScaledUnit o) {
return o == null || "none".equals(o.getStringValue()) || o.getNumericValue() == 0 ;
}
private static String hashString(String message, String algorithm) {
try {
MessageDigest digest = MessageDigest.getInstance(algorithm);
byte[] hashedBytes = digest.digest(message.getBytes("UTF-8"));
return convertByteArrayToHexString(hashedBytes);
} catch (NoSuchAlgorithmException | UnsupportedEncodingException ex) {
throw new RuntimeException(
"Could not generate hash from String", ex);
}
}
private static String convertByteArrayToHexString(byte[] arrayBytes) {
StringBuffer stringBuffer = new StringBuffer();
for (int i = 0; i < arrayBytes.length; i++) {
stringBuffer.append(Integer.toString((arrayBytes[i] & 0xff) + 0x100, 16)
.substring(1));
}
return stringBuffer.toString();
}
public static String generateMD5(String message) {
return hashString(message, "MD5");
}
public static String generateSHA1(String message) {
return hashString(message, "SHA-1");
}
public static String generateSHA256(String message) {
return hashString(message, "SHA-256");
}
public class Element {
Element parent = anyNodeStyle;
Map properties = new LinkedHashMap();
Element unselected;
Element selected;
Element pressed;
Element disabled;
public String getChecksum() {
StringBuilder sb = new StringBuilder();
sb.append("STYLE=").append(this.getFlattenedStyle())
.append(";UNSELECTED=").append(this.getFlattenedUnselectedStyle())
.append(";SELECTED=").append(this.getFlattenedSelectedStyle())
.append(";PRESSED=").append(this.getFlattenedPressedStyle())
.append(";DISABLED=").append(this.getFlattenedDisabledStyle());
return generateMD5(sb.toString());
}
Insets getBoxShadowPadding(Map style) {
Insets i = new Insets();
ScaledUnit boxShadow = (ScaledUnit)style.get("cn1-box-shadow-h");
ScaledUnit tmp = boxShadow;
while (tmp != null) {
tmp = (ScaledUnit)tmp.getPreviousLexicalUnit();
if (tmp != null) {
boxShadow = tmp;
}
}
if (isNone(boxShadow)) {
return i;
}
ScaledUnit insetUnit = boxShadow;
while (insetUnit != null) {
if ("inset".equals(insetUnit.getStringValue())) {
return i;
}
insetUnit = (ScaledUnit)insetUnit.getNextLexicalUnit();
}
double hShadow = boxShadow.getPixelValue();
boxShadow = (ScaledUnit)boxShadow.getNextLexicalUnit();
double vShadow = 0;
if (boxShadow == null) {
boxShadow = (ScaledUnit)style.get("cn1-box-shadow-v");
}
if (boxShadow != null) {
vShadow = boxShadow.getPixelValue();
boxShadow = (ScaledUnit)boxShadow.getNextLexicalUnit();
}
double blur = 0;
if (boxShadow == null) {
boxShadow = (ScaledUnit)style.get("cn1-box-shadow-blur");
}
if (boxShadow != null) {
blur = boxShadow.getPixelValue();
boxShadow = (ScaledUnit)boxShadow.getNextLexicalUnit();
}
double spread = 0;
if (boxShadow == null) {
boxShadow = (ScaledUnit)style.get("cn1-box-shadow-spread");
}
if (boxShadow != null) {
spread = boxShadow.getPixelValue();
}
i.top = Math.max(0,(int)Math.ceil(spread - vShadow + blur/2));
i.left = Math.max(0, (int)Math.ceil(spread - hShadow + blur/2));
i.bottom = Math.max(0, (int)Math.ceil(spread + vShadow + blur/2));
i.right = Math.max(0, (int)Math.ceil(spread + hShadow + blur/2));
return i;
}
String generateBoxShadowPaddingString() {
StringBuilder sb = new StringBuilder();
Map styles = new LinkedHashMap();
styles.putAll(getFlattenedStyle());
return ""+getBoxShadowPadding(styles);
}
String generateStyleCSS() {
Map styles = new LinkedHashMap();
styles.putAll(getFlattenedStyle());
try {
StringBuilder sb = new StringBuilder();
if (this.requiresImageBorder(styles)) {
if (styles.get("min-height") != null) {
styles.put("height", styles.get("min-height"));
}
if (styles.get("min-width") != null) {
styles.put("width", styles.get("min-width"));
}
}
if (styles.get("height") == null) {
styles.put("height", new ScaledUnit(new PixelUnit(100), 320, 640, 960));
}
//styles.put("margin", new ScaledUnit(new PixelUnit(1), 144, 640, 960));
for (Object key : styles.keySet()) {
String property = (String) key;
LexicalUnit value = (LexicalUnit) styles.get(key);
String prop = renderCSSProperty(property, styles);
if (!prop.isEmpty()) {
sb.append(prop).append(";");
}
}
Insets shadowInset = getBoxShadowPadding(styles);
if (shadowInset.top > 0) {
sb.append("margin-top: ").append(shadowInset.top).append("px !important;");
}
if (shadowInset.left > 0) {
sb.append("margin-left: ").append(shadowInset.left).append("px !important;");
}
if (shadowInset.right > 0) {
sb.append("margin-right: ").append(shadowInset.right).append("px !important;");
}
if (shadowInset.bottom > 0) {
sb.append("margin-bottom: ").append(shadowInset.bottom).append("px !important;");
}
//sb.append("border-top-right-radius: 10px / 20px;");
return sb.toString();
} catch (Exception ex) {
System.err.println("Failed to generate style CSS for style: " + styles + ". Message was "+ex.getMessage());
throw ex;
}
}
void setParent(String name) {
Element parentEl = getElementByName(name);
Element self = this;
if (this.isSelectedStyle() || this.isDisabledStyle() || this.isDisabledStyle() || this.isUnselectedStyle()) {
self = this.parent;
}
self.parent = parentEl;
}
String getHtmlPreview() {
StringBuilder sb = new StringBuilder();
sb.append("Lorem Ipsum");
return sb.toString();
}
public String getEmptyHtmlWithId(String id, Map style) {
StringBuilder sb = new StringBuilder();
String generateImage = (this.requiresBackgroundImageGeneration(style) || this.requiresImageBorder(style)) ? "true" : "false";
sb.append("");
return sb.toString();
}
Map getFlattenedSelectedStyle() {
Map out = new LinkedHashMap();
LinkedList
© 2015 - 2025 Weber Informatics LLC | Privacy Policy