com.hfg.css.CSS Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of com_hfg Show documentation
Show all versions of com_hfg Show documentation
com.hfg xml, html, svg, and bioinformatics utility library
package com.hfg.css;
import java.awt.Color;
import java.io.IOException;
import java.io.Reader;
import java.util.*;
import com.hfg.graphics.ColorUtil;
import com.hfg.html.HTMLTag;
import com.hfg.html.attribute.Align;
import com.hfg.html.attribute.HTMLColor;
import com.hfg.html.attribute.VAlign;
import com.hfg.util.*;
import com.hfg.util.collection.CollectionUtil;
//------------------------------------------------------------------------------
/**
Class to define some css style shortcuts.
Use example:
span.setStyle(CSS.BOLD + CSS.color(Color.red) + CSS.ITALIC);
@author J. Alex Taylor, hairyfatguy.com
*/
//------------------------------------------------------------------------------
// com.hfg XML/HTML Coding Library
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
// [email protected]
//------------------------------------------------------------------------------
public class CSS
{
//###########################################################################
// PUBLIC FIELDS
//###########################################################################
public static final String STYLE = "style";
/**
Shortcut for 'font-weight:bold;'
*/
public static final String BOLD = "font-weight:bold;";
/**
Shortcut for 'font-style:italic;'
*/
public static final String ITALIC = "font-style:italic;";
/**
Shortcut for 'text-decoration:underline;'
*/
public static final String UNDERLINE = "text-decoration:underline;";
/**
Shortcut for 'font-size:small;'
*/
public static final String SMALL_FONT = "font-size:small;";
/**
Shortcut for 'font-family:monospace;'
*/
public static final String MONOSPACE = "font-family:monospace;";
/**
Shortcut for 'vertical-align:super;'
*/
public static final String SUPER = "vertical-align:super;";
//###########################################################################
// PRIVATE FIELDS
//###########################################################################
private List mCSSRules;
//###########################################################################
// CONSTRUCTORS
//###########################################################################
//--------------------------------------------------------------------------
public CSS()
{
}
//--------------------------------------------------------------------------
/**
Parses CSS text into rules.
@param inReader CSS text
@throws IOException if a problem is encountered while processing the CSS text from the specified Reader
*/
public CSS(Reader inReader)
throws IOException
{
mCSSRules = parse(inReader);
}
//###########################################################################
// PUBLIC METHODS
//###########################################################################
//--------------------------------------------------------------------------
@Override
public String toString()
{
StringBuilderPlus buffer = new StringBuilderPlus();
if (CollectionUtil.hasValues(getCSSRules()))
{
for (CSSRule rule : getCSSRules())
{
buffer.appendln(rule.toString());
}
}
return buffer.toString();
}
//--------------------------------------------------------------------------
public CSSRule addRule()
{
CSSRule rule = new CSSRule();
addRule(rule);
return rule;
}
//--------------------------------------------------------------------------
public CSS addRule(CSSRule inRule)
{
if (null == mCSSRules)
{
mCSSRules = new ArrayList<>();
}
mCSSRules.add(inRule);
return this;
}
//--------------------------------------------------------------------------
public List getCSSRules()
{
return mCSSRules;
}
//--------------------------------------------------------------------------
/**
Returns CSS declarations that apply to the specified HTML tag.
Useful for fusing HTML and CSS before conversion (to WordprocessingML for example).
NOTE: This initial implementation does not handle complex selectors.
@param inHTML top tag in a chunk of HTML DOM
@param inMediaType CSS media type
@return List of CSS declarations
*/
//TODO: improve selector matching
public List getCSSDeclarationsForHTMLTag(HTMLTag inHTML, CSSMediaType inMediaType)
{
List declarations = new ArrayList<>(5);
for (CSSRule cssRule : mCSSRules)
{
// Does this CSS rule apply to this tag?
if (cssRule.appliesTo(inMediaType))
{
for (CSSSelector selector : cssRule.getSelectors())
{
if (selector.appliesTo(inHTML))
{
if (CollectionUtil.hasValues(cssRule.getDeclarations()))
{
declarations.addAll(cssRule.getDeclarations());
}
}
}
}
}
return declarations;
}
//--------------------------------------------------------------------------
/**
Where specified CSS rules apply, the styling is placed in the tag's style attribute.
Useful for fusing HTML and CSS before conversion (to WordprocessingML for example).
NOTE: This initial implementation does not handle complex selectors.
@param inHTML top tag in a chunk of HTML DOM
@param inMediaType CSS media type
*/
//TODO: improve selector matching
public void localizeStyles(HTMLTag inHTML, CSSMediaType inMediaType)
{
for (CSSRule cssRule : mCSSRules)
{
// Does this CSS rule apply to this tag?
if (cssRule.appliesTo(inMediaType))
{
for (CSSSelector selector : cssRule.getSelectors())
{
if (selector.appliesTo(inHTML))
{
// Since the local style css should override the stylesheet css we need to (re)add it last.
String tagStyle = inHTML.getStyle();
inHTML.setStyle(cssRule.getDeclarationString());
if (StringUtil.isSet(tagStyle))
{
inHTML.addStyle(tagStyle);
}
}
}
}
}
// Recurse thru subtags
List subtags = inHTML.getSubtags();
if (CollectionUtil.hasValues(subtags))
{
for (HTMLTag subtag : subtags)
{
localizeStyles(subtag, inMediaType);
}
}
}
//--------------------------------------------------------------------------
public static String color(String inColor)
{
return CSSProperty.color + ":" + (HexUtil.isHex(inColor) && ! inColor.startsWith("#") ? "#" : "") + inColor + ";";
}
//--------------------------------------------------------------------------
public static String color(Color inColor)
{
return CSSProperty.color + ":" + CssUtil.colorToCssValue(inColor) + ";";
}
//--------------------------------------------------------------------------
public static String bgColor(String inColor)
{
return CSSProperty.background_color + ":" + (HexUtil.isHex(inColor) && ! inColor.startsWith("#") ? "#" : "") + inColor + ";";
}
//--------------------------------------------------------------------------
public static String bgColor(Color inColor)
{
return CSSProperty.background_color + ":" + CssUtil.colorToCssValue(inColor) + ";";
}
//--------------------------------------------------------------------------
public static String fontSize(int inPtSize)
{
return CSSProperty.font_size + ":" + inPtSize + "pt;";
}
//--------------------------------------------------------------------------
public static String fontSizeEm(int inValue)
{
return CSSProperty.font_size + ":" + inValue + "em;";
}
//--------------------------------------------------------------------------
public static String width(String inValue)
{
return CSSProperty.width + ":" + inValue + ";";
}
//--------------------------------------------------------------------------
public static String height(String inValue)
{
return CSSProperty.height + ":" + inValue + ";";
}
//--------------------------------------------------------------------------
public static String textAlign(Align inValue)
{
return CSSProperty.text_align + ":" + inValue + ";";
}
//--------------------------------------------------------------------------
public static String verticalAlign(VAlign inValue)
{
return CSSProperty.vertical_align + ":" + inValue + ";";
}
//--------------------------------------------------------------------------
public static String display(CSSDisplayValue inValue)
{
return CSSProperty.display + ":" + inValue.getDisplayString() + ";";
}
//###########################################################################
// PRIVATE METHODS
//###########################################################################
//--------------------------------------------------------------------------
private List parse(Reader inReader)
throws IOException
{
List cssRules = new ArrayList<>();
CSSRule currentCSSRule = null;
Set currentMediaQueries = null;
boolean parsingMediaQueryList = false;
boolean parsingSelector = true;
boolean parsingDeclaration = false;
boolean inBlockComment = false;
StringBuilder mediaQueryListBuffer = new StringBuilder();
StringBuilder selectorBuffer = new StringBuilder();
StringBuilder declarationBuffer = new StringBuilder();
Character prevChar = null;
int currentChar;
while ((currentChar = inReader.read()) != -1)
{
if (inBlockComment)
{
if (currentChar == '/'
&& prevChar == '*')
{
inBlockComment = false;
}
}
else if (parsingMediaQueryList)
{
if (currentChar == '{')
{
parsingSelector = true;
parsingMediaQueryList = false;
String[] mediaQueries = mediaQueryListBuffer.toString().split(",");
currentMediaQueries = new HashSet(mediaQueries.length);
for (String mediaQueryString : mediaQueries)
{
currentMediaQueries.add(new CSSMediaQuery(mediaQueryString));
}
}
else
{
mediaQueryListBuffer.append((char)currentChar);
}
}
else if (parsingSelector
&& currentChar == 'a'
&& selectorBuffer.toString().trim().equals("@medi"))
{
parsingSelector = false;
parsingMediaQueryList = true;
selectorBuffer.setLength(0);
}
else if (currentChar == '*'
&& prevChar == '/')
{
inBlockComment = true;
// Back out the slash from the buffer
if (parsingSelector)
{
selectorBuffer.setLength(selectorBuffer.length() - 1);
}
else if (parsingDeclaration)
{
declarationBuffer.setLength(declarationBuffer.length() - 1);
}
}
else if (parsingSelector)
{
if (currentChar == ','
|| currentChar == '{')
{
if (null == currentCSSRule)
{
currentCSSRule = new CSSRule().setMediaQueries(currentMediaQueries);
}
currentCSSRule.addSelector(new CSSSelector(selectorBuffer.toString().trim()));
selectorBuffer.setLength(0);
if (currentChar == '{')
{
parsingSelector = false;
parsingDeclaration = true;
}
}
else if (currentChar == '}'
&& CollectionUtil.hasValues(currentMediaQueries))
{
currentMediaQueries = null;
mediaQueryListBuffer.setLength(0);
selectorBuffer.setLength(0);
}
else
{
selectorBuffer.append((char)currentChar);
}
}
else if (parsingDeclaration)
{
if (currentChar == ';'
|| currentChar == '}')
{
String declaration = declarationBuffer.toString().trim();
if (StringUtil.isSet(declaration))
{
String[] pieces = declaration.split(":");
if (2 == pieces.length)
{
CSSProperty cssProperty = CSSProperty.valueOf(pieces[0]);
currentCSSRule.addDeclaration(new CSSDeclaration(cssProperty, pieces[1]));
}
declarationBuffer.setLength(0);
}
if (currentChar == '}')
{
parsingSelector = true;
parsingDeclaration = false;
cssRules.add(currentCSSRule);
currentCSSRule = null;
}
}
else
{
declarationBuffer.append((char)currentChar);
}
}
prevChar = (char) currentChar;
}
return cssRules;
}
}