/*
* Copyright 2017 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.kie.workbench.common.stunner.svg.gen.translator.css;
import java.io.IOException;
import java.io.StringReader;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.steadystate.css.dom.CSSStyleRuleImpl;
import com.steadystate.css.dom.CSSStyleSheetImpl;
import com.steadystate.css.parser.CSSOMParser;
import com.steadystate.css.parser.SACParserCSS3;
import org.apache.commons.lang3.StringUtils;
import org.kie.workbench.common.stunner.svg.gen.exception.TranslatorException;
import org.kie.workbench.common.stunner.svg.gen.model.StyleDefinition;
import org.kie.workbench.common.stunner.svg.gen.model.TransformDefinition;
import org.kie.workbench.common.stunner.svg.gen.model.impl.StyleDefinitionImpl;
import org.kie.workbench.common.stunner.svg.gen.model.impl.TransformDefinitionImpl;
import org.w3c.css.sac.InputSource;
import org.w3c.dom.Element;
import org.w3c.dom.css.CSSRule;
import org.w3c.dom.css.CSSRuleList;
import org.w3c.dom.css.CSSStyleDeclaration;
public class SVGStyleTranslatorHelper {
private static final String TRANSFORM_SCALE = "scale";
private static final String TRANSFORM_TRANSLATE = "translate";
private static final Pattern TRANSFORM_PATTERN = Pattern.compile("(.*)\\((.*),(.*)\\)");
public static final String OPACITY = "opacity";
public static final String FILL = "fill";
public static final String FILL_OPACITY = "fill-opacity";
public static final String STROKE = "stroke";
public static final String STROKE_OPACITY = "stroke-opacity";
public static final String STROKE_WIDTH = "stroke-width";
public static final String STYLE = "style";
public static final String TRANSFORM = "transform";
public static final String ATTR_VALUE_NONE = "none";
public static final String[] ATTR_NAMES = new String[]{
OPACITY, FILL, FILL_OPACITY, STROKE, STROKE_OPACITY, STROKE_WIDTH
};
public static TransformDefinition parseTransformDefinition(final Element element) throws TranslatorException {
final String transformRaw = element.getAttribute(TRANSFORM);
if (!isEmpty(transformRaw)) {
final double[] t = parseTransform(transformRaw);
return new TransformDefinitionImpl(t[0],
t[1],
t[2],
t[3]);
}
return new TransformDefinitionImpl();
}
private static double[] parseTransform(final String raw) throws TranslatorException {
double sx = 1;
double sy = 1;
double tx = 0;
double ty = 0;
final String[] split = raw.split(" ");
for (final String transformDec : split) {
final Matcher m = TRANSFORM_PATTERN.matcher(transformDec);
if (m.matches()) {
final String op = m.group(1).trim();
final String x = m.group(2).trim();
final String y = m.group(3).trim();
switch (op) {
case TRANSFORM_SCALE:
sx = SVGAttributeParserUtils.toPixelValue(x);
sy = SVGAttributeParserUtils.toPixelValue(y);
break;
case TRANSFORM_TRANSLATE:
tx = SVGAttributeParserUtils.toPixelValue(x);
ty = SVGAttributeParserUtils.toPixelValue(y);
break;
}
} else {
throw new TranslatorException("Unrecognized transform attribute value format [" + raw + "]");
}
}
return new double[]{sx, sy, tx, ty};
}
// For now only single declaration support, the first one found.
public static StyleDefinition parseStyleDefinition(final Element element) throws TranslatorException {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < ATTR_NAMES.length; i++) {
final String key = ATTR_NAMES[i];
final String value = element.getAttribute(key);
if (!isEmpty(value)) {
builder.append(key).append(": ").append(value).append("; ");
}
}
final String styleRaw = element.getAttribute(STYLE);
if (!isEmpty(styleRaw)) {
builder.append(styleRaw);
}
if (0 < builder.length()) {
return parseStyleDefinition(builder.toString());
}
// Return default styles.
return new StyleDefinitionImpl.Builder().build();
}
public static StyleDefinition parseStyleDefinition(final String styleRaw) throws TranslatorException {
final CSSStyleSheetImpl sheet = parseStyleSheet(styleRaw);
final CSSRuleList cssRules = sheet.getCssRules();
for (int i = 0; i < cssRules.getLength(); i++) {
final CSSRule item = cssRules.item(i);
if (CSSRule.STYLE_RULE == item.getType()) {
final CSSStyleRuleImpl rule = (CSSStyleRuleImpl) item;
String selectorText = rule.getSelectorText();
System.out.println("- " + selectorText);
final CSSStyleDeclaration declaration = rule.getStyle();
return parseStyleDefinition(declaration);
}
}
return null;
}
private static CSSStyleSheetImpl parseStyleSheet(final String style) throws TranslatorException {
try {
final String declaration = ".shape { " + style + "}";
InputSource source = new InputSource(new StringReader(declaration));
CSSOMParser parser = new CSSOMParser(new SACParserCSS3());
return (CSSStyleSheetImpl) parser.parseStyleSheet(source,
null,
null);
} catch (final IOException e) {
throw new TranslatorException("Exception while parsing some style defintion.",
e);
}
}
private static StyleDefinition parseStyleDefinition(final CSSStyleDeclaration declaration) {
final StyleDefinitionImpl.Builder builder = new StyleDefinitionImpl.Builder();
boolean isFillNone = false;
boolean isStrokeNone = false;
for (int j = 0; j < declaration.getLength(); j++) {
final String property = declaration.item(j).trim();
final String value = declaration.getPropertyValue(property).trim();
switch (property) {
case OPACITY:
builder.setAlpha(SVGAttributeParserUtils.toPixelValue(value));
break;
case FILL:
if (ATTR_VALUE_NONE.equals(value)) {
isFillNone = true;
} else {
builder.setFillColor(SVGAttributeParserUtils.toHexColorString(value));
}
break;
case FILL_OPACITY:
builder.setFillAlpha(SVGAttributeParserUtils.toPixelValue(value));
break;
case STROKE:
if (ATTR_VALUE_NONE.equals(value)) {
isStrokeNone = true;
} else {
builder.setStrokeColor(SVGAttributeParserUtils.toHexColorString(value));
}
break;
case STROKE_OPACITY:
builder.setStrokeAlpha(SVGAttributeParserUtils.toPixelValue(value));
break;
case STROKE_WIDTH:
builder.setStrokeWidth(SVGAttributeParserUtils.toPixelValue(value));
break;
}
}
if (isFillNone) {
builder.setFillAlpha(0);
}
if (isStrokeNone) {
builder.setStrokeAlpha(0);
}
return builder.build();
}
private static boolean isEmpty(final String s) {
return StringUtils.isEmpty(s);
}
}