/*
GNU GENERAL LICENSE
Copyright (C) 2006 The Lobo Project. Copyright (C) 2014 - 2017 Lobo Evolution
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
verion 3 of the License, or (at your option) any later version.
This program 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 License for more details.
You should have received a copy of the GNU General Public
along with this program. If not, see <http://www.gnu.org/licenses/>.
Contact info: lobochief@users.sourceforge.net; ivan.difrancesco@yahoo.it
*/
package org.lobobrowser.html.style;
import java.awt.Color;
import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.awt.Insets;
import java.awt.Toolkit;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.lobobrowser.html.info.BorderInfo;
import org.lobobrowser.html.info.FontInfo;
import org.lobobrowser.html.renderstate.RenderState;
import org.lobobrowser.util.Urls;
import org.lobobrowser.util.gui.ColorFactory;
import org.lobobrowser.util.gui.LAFSettings;
import org.w3c.dom.css.CSS2Properties;
/**
* The Class HtmlValues.
*/
public class HtmlValues implements CSSValuesProperties{
/** The Constant SYSTEM_FONTS. */
public static final Map<String, FontInfo> SYSTEM_FONTS = new HashMap<String, FontInfo>();
/** The Constant logger. */
private static final Logger logger = LogManager.getLogger(HtmlValues.class.getName());
/** The Constant DEFAULT_BORDER_WIDTH. */
public static final int DEFAULT_BORDER_WIDTH = 2;
/** The Constant BORDER_STYLE_NONE. */
public static final int BORDER_STYLE_NONE = 0;
/** The Constant BORDER_STYLE_HIDDEN. */
public static final int BORDER_STYLE_HIDDEN = 1;
/** The Constant BORDER_STYLE_DOTTED. */
public static final int BORDER_STYLE_DOTTED = 2;
/** The Constant BORDER_STYLE_DASHED. */
public static final int BORDER_STYLE_DASHED = 3;
/** The Constant BORDER_STYLE_SOLID. */
public static final int BORDER_STYLE_SOLID = 4;
/** The Constant BORDER_STYLE_DOUBLE. */
public static final int BORDER_STYLE_DOUBLE = 5;
/** The Constant BORDER_STYLE_GROOVE. */
public static final int BORDER_STYLE_GROOVE = 6;
/** The Constant BORDER_STYLE_RIDGE. */
public static final int BORDER_STYLE_RIDGE = 7;
/** The Constant BORDER_STYLE_INSET. */
public static final int BORDER_STYLE_INSET = 8;
/** The Constant BORDER_STYLE_OUTSET. */
public static final int BORDER_STYLE_OUTSET = 9;
static {
FontInfo systemFont = new FontInfo();
SYSTEM_FONTS.put(CAPTION, systemFont);
SYSTEM_FONTS.put(ICON, systemFont);
SYSTEM_FONTS.put(MENU, systemFont);
SYSTEM_FONTS.put(MESSAGE_BOX, systemFont);
SYSTEM_FONTS.put(SMALL_CAPTION, systemFont);
SYSTEM_FONTS.put(STATUS_BAR, systemFont);
}
/**
* Instantiates a new html values.
*/
private HtmlValues() {
}
/**
* Checks if is border style.
*
* @param token
* the token
* @return true, if is border style
*/
public static boolean isBorderStyle(String token) {
String tokenTL = token.toLowerCase();
return tokenTL.equals(SOLID) || tokenTL.equals(DASHED)
|| tokenTL.equals(DOTTED) || tokenTL.equals(DOUBLE)
|| tokenTL.equals(NONE) || tokenTL.equals(HIDDEN)
|| tokenTL.equals(GROOVE) || tokenTL.equals(RIDGE)
|| tokenTL.equals(INSET) || tokenTL.equals(OUTSET);
}
/**
* Gets the margin insets.
*
* @param cssProperties
* the css properties
* @param renderState
* the render state
* @return the margin insets
*/
public static HtmlInsets getMarginInsets(CSS2Properties cssProperties, RenderState renderState) {
HtmlInsets insets = null;
String marginText = cssProperties.getMargin();
if (marginText != null) {
String[] mg = marginText.split(" ");
int sizeMargin = mg.length;
switch (sizeMargin) {
case 4:
insets = updateTopInset(insets, mg[0], renderState);
insets = updateRightInset(insets, mg[1], renderState);
insets = updateBottomInset(insets, mg[2], renderState);
insets = updateLeftInset(insets, mg[3], renderState);
break;
case 3:
insets = updateTopInset(insets, mg[0], renderState);
insets = updateRightInset(insets, mg[1], renderState);
insets = updateBottomInset(insets, mg[2], renderState);
break;
case 2:
insets = updateTopInset(insets, mg[0], renderState);
insets = updateRightInset(insets, mg[1], renderState);
break;
case 1:
insets = updateTopInset(insets, mg[0], renderState);
insets = updateRightInset(insets, mg[0], renderState);
insets = updateBottomInset(insets, mg[0], renderState);
insets = updateLeftInset(insets, mg[0], renderState);
break;
}
} else {
String topText = cssProperties.getMarginTop();
insets = updateTopInset(insets, topText, renderState);
String leftText = cssProperties.getMarginLeft();
insets = updateLeftInset(insets, leftText, renderState);
String bottomText = cssProperties.getMarginBottom();
insets = updateBottomInset(insets, bottomText, renderState);
String rightText = cssProperties.getMarginRight();
insets = updateRightInset(insets, rightText, renderState);
}
return insets;
}
/**
* Gets the padding insets.
*
* @param cssProperties
* the css properties
* @param renderState
* the render state
* @return the padding insets
*/
public static HtmlInsets getPaddingInsets(CSS2Properties cssProperties, RenderState renderState) {
HtmlInsets insets = null;
String paddingText = cssProperties.getPadding();
if (paddingText != null) {
String[] pd = paddingText.split(" ");
int sizePadding = pd.length;
switch (sizePadding) {
case 4:
insets = updateTopInset(insets, pd[0], renderState);
insets = updateRightInset(insets, pd[1], renderState);
insets = updateBottomInset(insets, pd[2], renderState);
insets = updateLeftInset(insets, pd[3], renderState);
break;
case 3:
insets = updateTopInset(insets, pd[0], renderState);
insets = updateRightInset(insets, pd[1], renderState);
insets = updateBottomInset(insets, pd[2], renderState);
break;
case 2:
insets = updateTopInset(insets, pd[0], renderState);
insets = updateRightInset(insets, pd[1], renderState);
break;
case 1:
insets = updateTopInset(insets, pd[0], renderState);
insets = updateRightInset(insets, pd[0], renderState);
insets = updateBottomInset(insets, pd[0], renderState);
insets = updateLeftInset(insets, pd[0], renderState);
break;
}
}else{
String topText = cssProperties.getPaddingTop();
insets = updateTopInset(insets, topText, renderState);
String leftText = cssProperties.getPaddingLeft();
insets = updateLeftInset(insets, leftText, renderState);
String bottomText = cssProperties.getPaddingBottom();
insets = updateBottomInset(insets, bottomText, renderState);
String rightText = cssProperties.getPaddingRight();
insets = updateRightInset(insets, rightText, renderState);
}
return insets;
}
/**
* Gets the border insets.
*
* @param borderStyles
* the border styles
* @param cssProperties
* the css properties
* @param renderState
* the render state
* @return the border insets
*/
public static HtmlInsets getBorderInsets(Insets borderStyles, CSS2Properties cssProperties,
RenderState renderState) {
HtmlInsets insets = null;
String borderText = cssProperties.getBorder();
if (borderText != null) {
String[] br = borderText.split(" ");
int sizeBorder = br.length;
switch (sizeBorder) {
case 4:
insets = updateTopInset(insets, br[0], renderState);
insets = updateRightInset(insets, br[1], renderState);
insets = updateBottomInset(insets, br[2], renderState);
insets = updateLeftInset(insets, br[3], renderState);
break;
case 3:
insets = updateTopInset(insets, br[0], renderState);
insets = updateRightInset(insets, br[1], renderState);
insets = updateBottomInset(insets, br[2], renderState);
break;
case 2:
insets = updateTopInset(insets, br[0], renderState);
insets = updateRightInset(insets, br[1], renderState);
break;
case 1:
insets = updateTopInset(insets, br[0], renderState);
insets = updateRightInset(insets, br[0], renderState);
insets = updateBottomInset(insets, br[0], renderState);
insets = updateLeftInset(insets, br[0], renderState);
break;
}
} else {
if (borderStyles.top != HtmlValues.BORDER_STYLE_NONE) {
String topText = cssProperties.getBorderTopWidth();
insets = updateTopInset(insets, topText, renderState);
}
if (borderStyles.left != HtmlValues.BORDER_STYLE_NONE) {
String leftText = cssProperties.getBorderLeftWidth();
insets = updateLeftInset(insets, leftText, renderState);
}
if (borderStyles.bottom != HtmlValues.BORDER_STYLE_NONE) {
String bottomText = cssProperties.getBorderBottomWidth();
insets = updateBottomInset(insets, bottomText, renderState);
}
if (borderStyles.right != HtmlValues.BORDER_STYLE_NONE) {
String rightText = cssProperties.getBorderRightWidth();
insets = updateRightInset(insets, rightText, renderState);
}
}
return insets;
}
/**
* Populates BorderInfo.insets.
*
* @param binfo
* A BorderInfo with its styles already populated.
* @param cssProperties
* The CSS properties object.
* @param renderState
* The current render state.
*/
public static void populateBorderInsets(BorderInfo binfo, CSS2Properties cssProperties, RenderState renderState) {
HtmlInsets insets = null;
if (binfo.getTopStyle() != HtmlValues.BORDER_STYLE_NONE) {
String topText = cssProperties.getBorderTopWidth();
insets = updateTopInset(insets, topText, renderState);
}
if (binfo.getLeftStyle() != HtmlValues.BORDER_STYLE_NONE) {
String leftText = cssProperties.getBorderLeftWidth();
insets = updateLeftInset(insets, leftText, renderState);
}
if (binfo.getBottomStyle() != HtmlValues.BORDER_STYLE_NONE) {
String bottomText = cssProperties.getBorderBottomWidth();
insets = updateBottomInset(insets, bottomText, renderState);
}
if (binfo.getRightStyle() != HtmlValues.BORDER_STYLE_NONE) {
String rightText = cssProperties.getBorderRightWidth();
insets = updateRightInset(insets, rightText, renderState);
}
binfo.setInsets(insets);
}
/**
* Update top inset.
*
* @param insets
* the insets
* @param sizeText
* the size text
* @param renderState
* the render state
* @return the html insets
*/
private static HtmlInsets updateTopInset(HtmlInsets insets, String sizeText, RenderState renderState) {
if (sizeText == null) {
sizeText = "2px";
}
sizeText = sizeText.trim();
if (sizeText.length() == 0) {
return insets;
}
if (insets == null) {
insets = new HtmlInsets();
}
if (AUTO.equalsIgnoreCase(sizeText)) {
insets.topType = HtmlInsets.TYPE_AUTO;
} else if(INHERIT.equalsIgnoreCase(sizeText)) {
if (renderState != null &&
renderState.getPreviousRenderState() != null &&
renderState.getPreviousRenderState().getMarginInsets() != null) {
insets.top = renderState.getPreviousRenderState().getMarginInsets().getTop();
insets.topType = renderState.getPreviousRenderState().getMarginInsets().getTopType();
}
} else if(INITIAL.equalsIgnoreCase(sizeText)) {
insets.topType = HtmlInsets.TYPE_PIXELS;
insets.top = HtmlValues.getPixelSize(sizeText, renderState, 0);
} else if(MEDIUM.equalsIgnoreCase(sizeText)) {
insets.topType = HtmlInsets.TYPE_PIXELS;
insets.top = HtmlValues.getPixelSize("3px", renderState, 0);
} else if (sizeText.endsWith("%")) {
insets.topType = HtmlInsets.TYPE_PERCENT;
try {
insets.top = Integer.parseInt(sizeText.substring(0, sizeText.length() - 1));
} catch (NumberFormatException nfe) {
insets.top = 0;
}
} else {
insets.topType = HtmlInsets.TYPE_PIXELS;
insets.top = HtmlValues.getPixelSize(sizeText, renderState, 0);
}
return insets;
}
/**
* Update left inset.
*
* @param insets
* the insets
* @param sizeText
* the size text
* @param renderState
* the render state
* @return the html insets
*/
private static HtmlInsets updateLeftInset(HtmlInsets insets, String sizeText, RenderState renderState) {
if (sizeText == null) {
sizeText = "2px";
}
sizeText = sizeText.trim();
if (sizeText.length() == 0) {
return insets;
}
if (insets == null) {
insets = new HtmlInsets();
}
if (AUTO.equalsIgnoreCase(sizeText)) {
insets.leftType = HtmlInsets.TYPE_AUTO;
} else if (INHERIT.equalsIgnoreCase(sizeText)) {
if (renderState != null &&
renderState.getPreviousRenderState() != null &&
renderState.getPreviousRenderState().getMarginInsets() != null) {
insets.left = renderState.getPreviousRenderState().getMarginInsets().getLeft();
insets.leftType = renderState.getPreviousRenderState().getMarginInsets().getLeftType();
}
} else if(INITIAL.equalsIgnoreCase(sizeText)) {
insets.leftType = HtmlInsets.TYPE_PIXELS;
insets.left = HtmlValues.getPixelSize(sizeText, renderState, 0);
} else if(MEDIUM.equalsIgnoreCase(sizeText)) {
insets.leftType = HtmlInsets.TYPE_PIXELS;
insets.left = HtmlValues.getPixelSize("3px", renderState, 0);
} else if (sizeText.endsWith("%")) {
insets.leftType = HtmlInsets.TYPE_PERCENT;
try {
insets.left = Integer.parseInt(sizeText.substring(0, sizeText.length() - 1));
} catch (NumberFormatException nfe) {
insets.left = 0;
}
} else {
insets.leftType = HtmlInsets.TYPE_PIXELS;
insets.left = HtmlValues.getPixelSize(sizeText, renderState, 0);
}
return insets;
}
/**
* Update bottom inset.
*
* @param insets
* the insets
* @param sizeText
* the size text
* @param renderState
* the render state
* @return the html insets
*/
private static HtmlInsets updateBottomInset(HtmlInsets insets, String sizeText, RenderState renderState) {
if (sizeText == null) {
sizeText = "2px";
}
sizeText = sizeText.trim();
if (sizeText.length() == 0) {
return insets;
}
if (insets == null) {
insets = new HtmlInsets();
}
if (AUTO.equalsIgnoreCase(sizeText)) {
insets.bottomType = HtmlInsets.TYPE_AUTO;
} else if (INHERIT.equalsIgnoreCase(sizeText)) {
if (renderState != null &&
renderState.getPreviousRenderState() != null &&
renderState.getPreviousRenderState().getMarginInsets() != null) {
insets.bottom = renderState.getPreviousRenderState().getMarginInsets().getBottom();
insets.bottomType = renderState.getPreviousRenderState().getMarginInsets().getBottomType();
}
} else if(INITIAL.equalsIgnoreCase(sizeText)) {
insets.bottomType = HtmlInsets.TYPE_PIXELS;
insets.bottom = HtmlValues.getPixelSize(sizeText, renderState, 0);
} else if(MEDIUM.equalsIgnoreCase(sizeText)) {
insets.bottomType = HtmlInsets.TYPE_PIXELS;
insets.bottom = HtmlValues.getPixelSize("3px", renderState, 0);
} else if (sizeText.endsWith("%")) {
insets.bottomType = HtmlInsets.TYPE_PERCENT;
try {
insets.bottom = Integer.parseInt(sizeText.substring(0, sizeText.length() - 1));
} catch (NumberFormatException nfe) {
insets.bottom = 0;
}
} else {
insets.bottomType = HtmlInsets.TYPE_PIXELS;
insets.bottom = HtmlValues.getPixelSize(sizeText, renderState, 0);
}
return insets;
}
/**
* Update right inset.
*
* @param insets
* the insets
* @param sizeText
* the size text
* @param renderState
* the render state
* @return the html insets
*/
private static HtmlInsets updateRightInset(HtmlInsets insets, String sizeText, RenderState renderState) {
if (sizeText == null) {
sizeText = "2px";
}
sizeText = sizeText.trim();
if (sizeText.length() == 0) {
return insets;
}
if (insets == null) {
insets = new HtmlInsets();
}
if (AUTO.equalsIgnoreCase(sizeText)) {
insets.rightType = HtmlInsets.TYPE_AUTO;
} else if (INHERIT.equalsIgnoreCase(sizeText)) {
if (renderState != null &&
renderState.getPreviousRenderState() != null &&
renderState.getPreviousRenderState().getMarginInsets() != null) {
insets.right = renderState.getPreviousRenderState().getMarginInsets().getRight();
insets.rightType = renderState.getPreviousRenderState().getMarginInsets().getRightType();
}
} else if(INITIAL.equalsIgnoreCase(sizeText)) {
insets.rightType = HtmlInsets.TYPE_PIXELS;
insets.right = HtmlValues.getPixelSize(sizeText, renderState, 0);
} else if(MEDIUM.equalsIgnoreCase(sizeText)) {
insets.rightType = HtmlInsets.TYPE_PIXELS;
insets.right = HtmlValues.getPixelSize("3px", renderState, 0);
} else if (sizeText.endsWith("%")) {
insets.rightType = HtmlInsets.TYPE_PERCENT;
try {
insets.right = Integer.parseInt(sizeText.substring(0, sizeText.length() - 1));
} catch (NumberFormatException nfe) {
insets.right = 0;
}
} else {
insets.rightType = HtmlInsets.TYPE_PIXELS;
insets.right = HtmlValues.getPixelSize(sizeText, renderState, 0);
}
return insets;
}
/**
* Gets the insets.
*
* @param insetsSpec
* the insets spec
* @param renderState
* the render state
* @param negativeOK
* the negative ok
* @return the insets
*/
public static Insets getInsets(String insetsSpec, RenderState renderState, boolean negativeOK) {
int[] insetsArray = new int[4];
int size = 0;
StringTokenizer tok = new StringTokenizer(insetsSpec);
if (tok.hasMoreTokens()) {
String token = tok.nextToken();
insetsArray[0] = getPixelSize(token, renderState, 0);
if (negativeOK || (insetsArray[0] >= 0)) {
size = 1;
if (tok.hasMoreTokens()) {
token = tok.nextToken();
insetsArray[1] = getPixelSize(token, renderState, 0);
if (negativeOK || (insetsArray[1] >= 0)) {
size = 2;
if (tok.hasMoreTokens()) {
token = tok.nextToken();
insetsArray[2] = getPixelSize(token, renderState, 0);
if (negativeOK || (insetsArray[2] >= 0)) {
size = 3;
if (tok.hasMoreTokens()) {
token = tok.nextToken();
insetsArray[3] = getPixelSize(token, renderState, 0);
size = 4;
if (negativeOK || (insetsArray[3] >= 0)) {
// nop
} else {
insetsArray[3] = 0;
}
}
} else {
size = 4;
insetsArray[2] = 0;
}
}
} else {
size = 4;
insetsArray[1] = 0;
}
}
} else {
size = 1;
insetsArray[0] = 0;
}
}
if (size == 4) {
return new Insets(insetsArray[0], insetsArray[3], insetsArray[2], insetsArray[1]);
} else if (size == 1) {
int val = insetsArray[0];
return new Insets(val, val, val, val);
} else if (size == 2) {
return new Insets(insetsArray[0], insetsArray[1], insetsArray[0], insetsArray[1]);
} else if (size == 3) {
return new Insets(insetsArray[0], insetsArray[1], insetsArray[2], insetsArray[1]);
} else {
return null;
}
}
/**
* Gets a number for 1 to 7.
*
* @param oldHtmlSpec
* A number from 1 to 7 or +1, etc.
* @param renderState
* the render state
* @return the font number old style
*/
public static final int getFontNumberOldStyle(String oldHtmlSpec, RenderState renderState) {
oldHtmlSpec = oldHtmlSpec.trim();
int tentative;
try {
if (oldHtmlSpec.startsWith("+")) {
tentative = renderState.getFontBase() + Integer.parseInt(oldHtmlSpec.substring(1));
} else if (oldHtmlSpec.startsWith("-")) {
tentative = renderState.getFontBase() + Integer.parseInt(oldHtmlSpec);
} else {
tentative = Integer.parseInt(oldHtmlSpec);
}
if (tentative < 1) {
tentative = 1;
} else if (tentative > 7) {
tentative = 7;
}
} catch (NumberFormatException nfe) {
// ignore
tentative = 3;
}
return tentative;
}
/**
* Gets the font size.
*
* @param fontNumber
* the font number
* @return the font size
*/
public static final float getFontSize(int fontNumber) {
switch (fontNumber) {
case 1:
return 10.0f;
case 2:
return 11.0f;
case 3:
return 13.0f;
case 4:
return 16.0f;
case 5:
return 21.0f;
case 6:
return 29.0f;
case 7:
return 42.0f;
default:
return 63.0f;
}
}
/**
* Gets the font size spec.
*
* @param fontNumber
* the font number
* @return the font size spec
*/
public static final String getFontSizeSpec(int fontNumber) {
switch (fontNumber) {
case 1:
return "10px";
case 2:
return "11px";
case 3:
return "13px";
case 4:
return "16px";
case 5:
return "21px";
case 6:
return "29px";
case 7:
return "42px";
default:
return "63px";
}
}
/**
* Gets the font size.
*
* @param spec
* the spec
* @param parentRenderState
* the parent render state
* @return the font size
*/
public static final float getFontSize(String spec, RenderState parentRenderState) {
String specTL = spec.toLowerCase();
if (specTL.endsWith("em")) {
if (parentRenderState == null) {
return LAFSettings.getInstance().getFontSize();
}
Font font = parentRenderState.getFont();
String pxText = specTL.substring(0, specTL.length() - 2);
double value;
try {
value = Double.parseDouble(pxText);
} catch (NumberFormatException nfe) {
return LAFSettings.getInstance().getFontSize();
}
return (int) Math.round(font.getSize() * value);
} else if (specTL.endsWith("px") || specTL.endsWith("pt") || specTL.endsWith("em") || specTL.endsWith("pc")
|| specTL.endsWith("em") || specTL.endsWith("mm") || specTL.endsWith("ex")) {
int pixelSize = getPixelSize(spec, parentRenderState, (int)LAFSettings.getInstance().getFontSize());
int dpi = GraphicsEnvironment.isHeadless() ? 72 : Toolkit.getDefaultToolkit().getScreenResolution();
// Normally the factor below should be 72, but
// the font-size concept in HTML is handled differently.
return (pixelSize * 96) / dpi;
} else if (specTL.endsWith("%")) {
String value = specTL.substring(0, specTL.length() - 1);
try {
double valued = Double.parseDouble(value);
double parentFontSize = parentRenderState == null ? 14.0 : parentRenderState.getFont().getSize();
return (float) ((parentFontSize * valued) / 100.0);
} catch (NumberFormatException nfe) {
return LAFSettings.getInstance().getFontSize();
}
} else {
int parentFontSize = 0;
switch (specTL) {
case SMALL:
return 12.0f;
case MEDIUM:
return 14.0f;
case LARGE:
return 20.0f;
case X_SMALL:
return 11.0f;
case XX_SMALL:
return 10.0f;
case X_LARGE:
return 26.0f;
case XX_LARGE:
return 40.0f;
case LARGER:
parentFontSize = (int) LAFSettings.getInstance().getFontSize();
if (parentRenderState != null)
parentFontSize = parentRenderState.getFont().getSize();
return parentFontSize * 1.2f;
case SMALLER:
parentFontSize = (int) LAFSettings.getInstance().getFontSize();
if (parentRenderState != null)
parentFontSize = parentRenderState.getFont().getSize();
return parentFontSize / 1.2f;
case INHERIT:
parentFontSize = (int) LAFSettings.getInstance().getFontSize();
if (parentRenderState != null)
parentRenderState.getPreviousRenderState().getFont().getSize();
return parentFontSize;
case INITIAL:
return getPixelSize(spec, parentRenderState, (int) LAFSettings.getInstance().getFontSize());
default:
return getPixelSize(spec, parentRenderState, (int) LAFSettings.getInstance().getFontSize());
}
}
}
/**
* Gets the pixel size.
*
* @param spec
* the spec
* @param renderState
* the render state
* @param errorValue
* the error value
* @param availSize
* the avail size
* @return the pixel size
*/
public static final int getPixelSize(String spec, RenderState renderState, int errorValue, int availSize) {
if (spec.endsWith("%")) {
String perText = spec.substring(0, spec.length() - 1);
try {
double val = Double.parseDouble(perText);
return (int) Math.round((availSize * val) / 100.0);
} catch (NumberFormatException nfe) {
return errorValue;
}
} else {
return getPixelSize(spec, renderState, errorValue);
}
}
/**
* Gets the pixel size.
*
* @param spec
* the spec
* @param renderState
* the render state
* @param errorValue
* the error value
* @return the pixel size
*/
public static final int getPixelSize(String spec, RenderState renderState, int errorValue) {
if(spec== null){
return 0;
}
String lcSpec = spec.toLowerCase();
if (lcSpec.endsWith("px")) {
String pxText = lcSpec.substring(0, lcSpec.length() - 2);
try {
return (int) Math.round(Double.parseDouble(pxText));
} catch (NumberFormatException nfe) {
return errorValue;
}
} else if (lcSpec.endsWith("em") && (renderState != null)) {
Font f = renderState.getFont();
String valText = lcSpec.substring(0, lcSpec.length() - 2);
double val;
try {
val = Double.parseDouble(valText);
} catch (NumberFormatException nfe) {
return errorValue;
}
// Get fontSize in 1/72 of an inch.
int fontSize = f.getSize();
int dpi = GraphicsEnvironment.isHeadless() ? 72 : Toolkit.getDefaultToolkit().getScreenResolution();
// The factor below should normally be 72, but font sizes
// are calculated differently in HTML.
double pixelSize = (fontSize * dpi) / 96;
return (int) Math.round(pixelSize * val);
} else if (lcSpec.endsWith("pt")) {
String valText = lcSpec.substring(0, lcSpec.length() - 2);
double val;
try {
val = Double.parseDouble(valText);
} catch (NumberFormatException nfe) {
return errorValue;
}
int dpi = GraphicsEnvironment.isHeadless() ? 72 : Toolkit.getDefaultToolkit().getScreenResolution();
double inches = val / 72;
return (int) Math.round(dpi * inches);
} else if (lcSpec.endsWith("pc")) {
String valText = lcSpec.substring(0, lcSpec.length() - 2);
double val;
try {
val = Double.parseDouble(valText);
} catch (NumberFormatException nfe) {
return errorValue;
}
int dpi = GraphicsEnvironment.isHeadless() ? 72 : Toolkit.getDefaultToolkit().getScreenResolution();
double inches = val / 6;
return (int) Math.round(dpi * inches);
} else if (lcSpec.endsWith("em")) {
String valText = lcSpec.substring(0, lcSpec.length() - 2);
double val;
try {
val = Double.parseDouble(valText);
} catch (NumberFormatException nfe) {
return errorValue;
}
int dpi = GraphicsEnvironment.isHeadless() ? 72 : Toolkit.getDefaultToolkit().getScreenResolution();
double inches = val / 2.54;
return (int) Math.round(dpi * inches);
} else if (lcSpec.endsWith("mm")) {
String valText = lcSpec.substring(0, lcSpec.length() - 2);
double val;
try {
val = Double.parseDouble(valText);
} catch (NumberFormatException nfe) {
return errorValue;
}
int dpi = GraphicsEnvironment.isHeadless() ? 72 : Toolkit.getDefaultToolkit().getScreenResolution();
double inches = val / 25.4;
return (int) Math.round(dpi * inches);
} else if (lcSpec.endsWith("ex") && (renderState != null)) {
// Factor below is to try to match size in other browsers.
double xHeight = renderState.getFontMetrics().getAscent() * 0.47;
String valText = lcSpec.substring(0, lcSpec.length() - 2);
double val;
try {
val = Double.parseDouble(valText);
} catch (NumberFormatException nfe) {
return errorValue;
}
return (int) Math.round(xHeight * val);
} else {
String pxText = lcSpec;
try {
return (int) Math.round(Double.parseDouble(pxText));
} catch (NumberFormatException nfe) {
return errorValue;
}
}
}
/**
* Gets the URI from style value.
*
* @param fullURLStyleValue
* the full url style value
* @return the URI from style value
*/
public static URL getURIFromStyleValue(String fullURLStyleValue) {
String start = "url(";
if (!fullURLStyleValue.toLowerCase().startsWith(start)) {
return null;
}
int startIdx = start.length();
int closingIdx = fullURLStyleValue.lastIndexOf(')');
if (closingIdx == -1) {
return null;
}
String quotedUri = fullURLStyleValue.substring(startIdx, closingIdx);
String tentativeUri = unquoteAndUnescape(quotedUri);
try {
return Urls.createURL(null, tentativeUri);
} catch (MalformedURLException | UnsupportedEncodingException mfu) {
logger.error("Unable to create URL for URI=[" + tentativeUri + "].", mfu);
return null;
}
}
/**
* Unquote and unescape.
*
* @param text
* the text
* @return the string
*/
public static String unquoteAndUnescape(String text) {
StringBuffer result = new StringBuffer();
int index = 0;
int length = text.length();
boolean escape = false;
boolean single = false;
if (index < length) {
char ch = text.charAt(index);
switch (ch) {
case '\'':
single = true;
break;
case '"':
break;
case '\\':
escape = true;
break;
default:
result.append(ch);
}
index++;
}
OUTER: for (; index < length; index++) {
char ch = text.charAt(index);
switch (ch) {
case '\'':
if (escape || !single) {
escape = false;
result.append(ch);
} else {
break OUTER;
}
break;
case '"':
if (escape || single) {
escape = false;
result.append(ch);
} else {
break OUTER;
}
break;
case '\\':
if (escape) {
escape = false;
result.append(ch);
} else {
escape = true;
}
break;
default:
if (escape) {
escape = false;
result.append('\\');
}
result.append(ch);
}
}
return result.toString();
}
/**
* Quote and escape.
*
* @param text
* the text
* @return the string
*/
public static String quoteAndEscape(String text) {
StringBuffer result = new StringBuffer();
result.append("'");
int index = 0;
int length = text.length();
while (index < length) {
char ch = text.charAt(index);
switch (ch) {
case '\'':
result.append("\\'");
break;
case '\\':
result.append("\\\\");
break;
default:
result.append(ch);
}
index++;
}
result.append("'");
return result.toString();
}
/**
* Gets the color from background.
*
* @param background
* the background
* @return the color from background
*/
public static String getColorFromBackground(String background) {
String[] backgroundParts = HtmlValues.splitCssValue(background);
for (int i = 0; i < backgroundParts.length; i++) {
String token = backgroundParts[i];
if (ColorFactory.getInstance().isColor(token)) {
return token;
}
}
return null;
}
/**
* Checks if is length.
*
* @param token
* the token
* @return true, if is length
*/
public static boolean isLength(String token) {
if (token.endsWith("px") || token.endsWith("pt") || token.endsWith("pc") || token.endsWith("em")
|| token.endsWith("mm") || token.endsWith("ex") || token.endsWith("em")) {
return true;
}
try {
Double.parseDouble(token);
return true;
} catch (NumberFormatException nfe) {
return false;
}
}
/**
* Split css value.
*
* @param cssValue
* the css value
* @return the string[]
*/
public static String[] splitCssValue(String cssValue) {
ArrayList<String> tokens = new ArrayList<String>(4);
int len = cssValue.length();
int parenCount = 0;
StringBuffer currentWord = null;
for (int i = 0; i < len; i++) {
char ch = cssValue.charAt(i);
switch (ch) {
case '(':
parenCount++;
if (currentWord == null) {
currentWord = new StringBuffer();
}
currentWord.append(ch);
break;
case ')':
parenCount--;
if (currentWord == null) {
currentWord = new StringBuffer();
}
currentWord.append(ch);
break;
case ' ':
case '\t':
case '\n':
case '\r':
if (parenCount == 0) {
tokens.add(currentWord.toString());
currentWord = null;
break;
} else {
// Fall through - no break
}
default:
if (currentWord == null) {
currentWord = new StringBuffer();
}
currentWord.append(ch);
break;
}
}
if (currentWord != null) {
tokens.add(currentWord.toString());
}
return tokens.toArray(new String[tokens.size()]);
}
/**
* Checks if is url.
*
* @param token
* the token
* @return true, if is url
*/
public static boolean isUrl(String token) {
return token.toLowerCase().startsWith("url(");
}
/**
* Gets the list style type.
*
* @param token
* the token
* @return the list style type
*/
public static int getListStyleType(String token) {
String tokenTL = token.toLowerCase();
switch (tokenTL) {
case NONE:
return ListStyle.TYPE_NONE;
case DISC:
return ListStyle.TYPE_DISC;
case CIRCLE:
return ListStyle.TYPE_CIRCLE;
case SQUARE:
return ListStyle.TYPE_SQUARE;
case DECIMAL:
return ListStyle.TYPE_DECIMAL;
case DECIMAL_LEADING_ZERO:
return ListStyle.TYPE_DECIMAL_LEADING_ZERO;
case LOWER_ALPHA:
return ListStyle.TYPE_LOWER_ALPHA;
case LOWER_LATIN :
return ListStyle.TYPE_LOWER_ALPHA;
case UPPER_ALPHA:
return ListStyle.TYPE_UPPER_ALPHA;
case UPPER_LATIN:
return ListStyle.TYPE_UPPER_ALPHA;
case LOWER_ROMAN:
return ListStyle.TYPE_LOWER_ROMAN;
case UPPER_ROMAN:
return ListStyle.TYPE_UPPER_ROMAN;
case INITIAL:
return ListStyle.TYPE_UPPER_ROMAN;
default:
return ListStyle.TYPE_UNSET;
}
}
/**
* Gets the list style position.
*
* @param token
* the token
* @return the list style position
*/
public static int getListStylePosition(String token) {
String tokenTL = token.toLowerCase();
if (INSIDE.equals(tokenTL)) {
return ListStyle.POSITION_INSIDE;
} else if (OUTSIDE.equals(tokenTL)) {
return ListStyle.POSITION_OUTSIDE;
} else {
return ListStyle.POSITION_UNSET;
}
}
/**
* Gets the list style.
*
* @param listStyleText
* the list style text
* @return the list style
*/
public static ListStyle getListStyle(String listStyleText) {
ListStyle listStyle = new ListStyle();
String[] tokens = HtmlValues.splitCssValue(listStyleText);
for (int i = 0; i < tokens.length; i++) {
String token = tokens[i];
int listStyleType = HtmlValues.getListStyleType(token);
if (listStyleType != ListStyle.TYPE_UNSET) {
listStyle.type = listStyleType;
} else if (HtmlValues.isUrl(token)) {
// TODO: listStyle.image
} else {
int listStylePosition = HtmlValues.getListStylePosition(token);
if (listStylePosition != ListStyle.POSITION_UNSET) {
listStyle.position = listStylePosition;
}
}
}
return listStyle;
}
/**
* Checks if is font style.
*
* @param token
* the token
* @return true, if is font style
*/
public static boolean isFontStyle(String token) {
return ITALIC.equals(token) || NORMAL.equals(token)
|| OBLIQUE.equals(token);
}
/**
* Checks if is font variant.
*
* @param token
* the token
* @return true, if is font variant
*/
public static boolean isFontVariant(String token) {
return SMALL_CAPS.equals(token) || NORMAL.equals(token);
}
/**
* Checks if is font weight.
*
* @param token
* the token
* @return true, if is font weight
*/
public static boolean isFontWeight(String token) {
if (BOLD.equals(token) || BOLDER.equals(token)
|| LIGHTER.equals(token)) {
return true;
}
try {
int value = Integer.parseInt(token);
return ((value % 100) == 0) && (value >= 100) && (value <= 900);
} catch (NumberFormatException nfe) {
return false;
}
}
/**
* Gets the border info.
*
* @param properties
* the properties
* @param renderState
* the render state
* @return the border info
*/
public static BorderInfo getBorderInfo(CSS2Properties properties, RenderState renderState) {
BorderInfo binfo = new BorderInfo();
if(INHERIT.equals(properties.getBorderTopStyle())){
binfo.setTopStyle(renderState.getPreviousRenderState().getBorderInfo().getTopStyle());
binfo.setTopColor(renderState.getPreviousRenderState().getBorderInfo().getTopColor());
}else{
binfo.setTopStyle(getBorderStyle(properties.getBorderTopStyle()));
binfo.setTopColor(getBorderColor(properties.getBorderTopColor(), properties, binfo));
}
if(INHERIT.equals(properties.getBorderBottomStyle())){
binfo.setBottomStyle(renderState.getPreviousRenderState().getBorderInfo().getBottomStyle());
binfo.setBottomColor(renderState.getPreviousRenderState().getBorderInfo().getBottomColor());
}else{
binfo.setBottomStyle(getBorderStyle(properties.getBorderBottomStyle()));
binfo.setBottomColor(getBorderColor(properties.getBorderBottomColor(), properties, binfo));
}
if(INHERIT.equals(properties.getBorderRightStyle())){
binfo.setRightStyle(renderState.getPreviousRenderState().getBorderInfo().getRightStyle());
binfo.setRightColor(renderState.getPreviousRenderState().getBorderInfo().getRightColor());
}else{
binfo.setRightStyle(getBorderStyle(properties.getBorderRightStyle()));
binfo.setRightColor(getBorderColor(properties.getBorderRightColor(), properties, binfo));
}
if(INHERIT.equals(properties.getBorderLeftStyle())){
binfo.setLeftStyle(renderState.getPreviousRenderState().getBorderInfo().getLeftStyle());
binfo.setLeftColor(renderState.getPreviousRenderState().getBorderInfo().getLeftColor());
}else{
binfo.setLeftStyle(getBorderStyle(properties.getBorderLeftStyle()));
binfo.setLeftColor(getBorderColor(properties.getBorderLeftColor(), properties, binfo));
}
HtmlValues.populateBorderInsets(binfo, properties, renderState);
return binfo;
}
/**
* Gets the border style.
*
* @param styleText
* the style text
* @return the border style
*/
private static int getBorderStyle(String styleText) {
if ((styleText == null) || (styleText.length() == 0)) {
return HtmlValues.BORDER_STYLE_NONE;
}
String stl = styleText.toLowerCase();
switch (stl) {
case SOLID:
return BORDER_STYLE_SOLID;
case DASHED:
return BORDER_STYLE_DASHED;
case DOTTED:
return BORDER_STYLE_DOTTED;
case NONE:
return BORDER_STYLE_NONE;
case HIDDEN:
return BORDER_STYLE_HIDDEN;
case DOUBLE:
return BORDER_STYLE_DOUBLE;
case GROOVE:
return BORDER_STYLE_GROOVE;
case RIDGE:
return BORDER_STYLE_RIDGE;
case INSET:
return BORDER_STYLE_INSET;
case OUTSET:
return BORDER_STYLE_OUTSET;
default:
return BORDER_STYLE_NONE;
}
}
/**
* Gets the border color.
*
* @param color
* the color
* @param properties
* the properties
* @param binfo
* the binfo
* @return the border color
*/
private static Color getBorderColor(String color, CSS2Properties properties, BorderInfo binfo) {
ColorFactory cf = ColorFactory.getInstance();
if (color != null && properties.getBorderColor() == null && properties.getColor() == null) {
return cf.getColor(color);
}
if (color != null && properties.getColor() != null) {
return cf.getColor(properties.getColor());
}
if (color != null && properties.getBorderColor() != null) {
return cf.getColor(properties.getBorderColor());
}
return LAFSettings.getInstance().getColor();
}
/**
* Checks if is background repeat.
*
* @param repeat
* the repeat
* @return true, if is background repeat
*/
public static boolean isBackgroundRepeat(String repeat) {
String repeatTL = repeat.toLowerCase();
return repeatTL.indexOf(REPEAT) != -1;
}
/**
* Checks if is background position.
*
* @param token
* the token
* @return true, if is background position
*/
public static boolean isBackgroundPosition(String token) {
return isLength(token) || token.endsWith("%") || token.equalsIgnoreCase(TOP)
|| token.equalsIgnoreCase(CENTER)
|| token.equalsIgnoreCase(BOTTOM)
|| token.equalsIgnoreCase(LEFT)
|| token.equalsIgnoreCase(RIGHT);
}
}