diff -r 7af6bad1fda3 javafx-ui-common/src/com/sun/javafx/css/StyleManager.java --- a/javafx-ui-common/src/com/sun/javafx/css/StyleManager.java Wed Dec 12 17:26:28 2012 -0800 +++ b/javafx-ui-common/src/com/sun/javafx/css/StyleManager.java Mon Dec 17 17:03:44 2012 -0800 @@ -25,6 +25,7 @@ package com.sun.javafx.css; +import java.awt.font.FontRenderContext; import java.io.IOException; import java.net.MalformedURLException; import java.net.URI; @@ -64,6 +65,7 @@ import javafx.collections.*; import javafx.collections.ListChangeListener.Change; import javafx.scene.Parent; +import javafx.scene.text.Font; import javafx.stage.PopupWindow; import javafx.stage.Window; @@ -810,29 +812,33 @@ try { final String ext = (parse) ? (".css") : (".bss"); + java.net.URL url = null; + Stylesheet stylesheet = null; + // check if url has extension, if not then just url as is and always parse as css text + if (!(fname.endsWith(".css") || fname.endsWith(".bss"))) { + url = getURL(fname); + parse = true; + } else { + final String name = fname.substring(0, fname.length() - 4); - final String name = - (fname.endsWith(".css") || fname.endsWith(".bss")) ? - fname.substring(0, fname.length() - 4) : fname; - - java.net.URL url = getURL(name+ext); - if (url == null && (parse = !parse)) { - // If we failed to get the URL for the .bss file, - // fall back to the .css file. - // Note that 'parse' is toggled in the test. - url = getURL(name+".css"); - } - - Stylesheet stylesheet = null; - if ((url != null) && !parse) { - stylesheet = Stylesheet.loadBinary(url); - - if (stylesheet == null && (parse = !parse)) { - // If we failed to load the .bss file, + url = getURL(name+ext); + if (url == null && (parse = !parse)) { + // If we failed to get the URL for the .bss file, // fall back to the .css file. // Note that 'parse' is toggled in the test. url = getURL(name+".css"); } + + if ((url != null) && !parse) { + stylesheet = Stylesheet.loadBinary(url); + + if (stylesheet == null && (parse = !parse)) { + // If we failed to load the .bss file, + // fall back to the .css file. + // Note that 'parse' is toggled in the test. + url = getURL(fname); + } + } } // either we failed to load the .bss file, or parse @@ -855,6 +861,18 @@ ); } } + + // load any fonts from @font-face + faceLoop: for(FontFace fontFace: stylesheet.getFontFaces()) { + for(FontFace.FontFaceSrc src: fontFace.getSources()) { + if (src.getType() == FontFace.FontFaceSrcType.URL) { + Font loadedFont = Font.loadFont(src.getSrc(),10); + getLogger().info("Loaded @font-face font [" + (loadedFont == null ? "null" : loadedFont.getName()) + "]"); + continue faceLoop; + } + } + } + return stylesheet; } catch (FileNotFoundException fnfe) { diff -r 7af6bad1fda3 javafx-ui-common/src/com/sun/javafx/css/Stylesheet.java --- a/javafx-ui-common/src/com/sun/javafx/css/Stylesheet.java Wed Dec 12 17:26:28 2012 -0800 +++ b/javafx-ui-common/src/com/sun/javafx/css/Stylesheet.java Mon Dec 17 17:03:44 2012 -0800 @@ -92,6 +92,9 @@ } }; + /** List of all font faces */ + private final List fontFaces = new ArrayList(); + /** * Constructs a stylesheet with the base URI defaulting to the root * path of the application. @@ -127,6 +130,10 @@ return rules; } + public List getFontFaces() { + return fontFaces; + } + @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj instanceof Stylesheet) { diff -r 7af6bad1fda3 javafx-ui-common/src/com/sun/javafx/css/parser/CSSLexer.java --- a/javafx-ui-common/src/com/sun/javafx/css/parser/CSSLexer.java Wed Dec 12 17:26:28 2012 -0800 +++ b/javafx-ui-common/src/com/sun/javafx/css/parser/CSSLexer.java Mon Dec 17 17:03:44 2012 -0800 @@ -73,6 +73,7 @@ final static int IMPORTANT_SYM = 39; final static int WS = 40; final static int NL = 41; + final static int FONT_FACE = 42; private final Recognizer A = new SimpleRecognizer('a','A'); private final Recognizer B = new SimpleRecognizer('b','B'); @@ -608,6 +609,7 @@ try { while (true) { + charNotConsumed = false; final LexerState[] reachableStates = currentState != null ? stateMap.get(currentState) : null; @@ -698,6 +700,7 @@ // not a comment - a SOLIDUS token = new Token(SOLIDUS,"/", line, offset); offset = pos; + charNotConsumed = true; } break; @@ -788,15 +791,27 @@ return tok; case '@': - // Skip over @IMPORT, etc. + // read word after '@' symbol + StringBuilder keywordSB = new StringBuilder(); do { ch = readChar(); - } while (ch != ';' && - ch != Token.EOF); - if (ch == ';') { - ch = readChar(); - token = Token.SKIP_TOKEN; + keywordSB.append((char)ch); + } while (!WS_CHARS.recognize(ch) && ch != Token.EOF); + String keyword = keywordSB.substring(0,keywordSB.length()-1); + if ("font-face".equalsIgnoreCase(keyword)) { + token = new Token(FONT_FACE,"@font-face", line, offset); offset = pos; + } else { + // Skip over @IMPORT, etc. + do { + ch = readChar(); + } while (ch != ';' && + ch != Token.EOF); + if (ch == ';') { + ch = readChar(); + token = Token.SKIP_TOKEN; + offset = pos; + } } break; @@ -815,7 +830,7 @@ return token; } - if (ch != -1) ch = readChar(); + if (ch != -1 && !charNotConsumed) ch = readChar(); final Token tok = token; token = null; @@ -828,6 +843,7 @@ } private int ch; + private boolean charNotConsumed = false; private Reader reader; private Token token; private final Map stateMap; diff -r 7af6bad1fda3 javafx-ui-common/src/com/sun/javafx/css/parser/CSSParser.java --- a/javafx-ui-common/src/com/sun/javafx/css/parser/CSSParser.java Wed Dec 12 17:26:28 2012 -0800 +++ b/javafx-ui-common/src/com/sun/javafx/css/parser/CSSParser.java Mon Dec 17 17:03:44 2012 -0800 @@ -58,6 +58,7 @@ import com.sun.javafx.css.CompoundSelector; import com.sun.javafx.css.CssError; import com.sun.javafx.css.Declaration; +import com.sun.javafx.css.FontFace; import com.sun.javafx.css.FontUnits; import com.sun.javafx.css.ParsedValue; import com.sun.javafx.css.Rule; @@ -3594,6 +3595,13 @@ while ((currentToken != null) && (currentToken.getType() != Token.EOF)) { + if (currentToken.getType() == CSSLexer.FONT_FACE) { + FontFace fontFace = fontFace(lexer); + if (fontFace != null) stylesheet.getFontFaces().add(fontFace); + currentToken = nextToken(lexer); + continue; + } + List selectors = selectors(lexer); if (selectors == null) return; @@ -3643,6 +3651,145 @@ currentToken = null; } + private FontFace fontFace(CSSLexer lexer) { + final Map descriptors = new HashMap(); + final List sources = new ArrayList(); + while(true) { + currentToken = nextToken(lexer); + if (currentToken.getType() == CSSLexer.IDENT) { + String key = currentToken.getText(); + // ignore the colon that follows + currentToken = nextToken(lexer); + // get the next token after colon + currentToken = nextToken(lexer); + // ignore all but "src" + if ("src".equalsIgnoreCase(key)) { + while(true) { + if((currentToken != null) && (currentToken.getType() != CSSLexer.SEMI) && + (currentToken.getType() != Token.EOF)) { + + if (currentToken.getType() == CSSLexer.IDENT) { + // simple reference to other font-family + sources.add(new FontFace.FontFaceSrc(FontFace.FontFaceSrcType.REFERENCE,currentToken.getText())); + } else if (currentToken.getType() == CSSLexer.FUNCTION) { + if ("url(".equalsIgnoreCase(currentToken.getText())) { + // consume the function token + currentToken = nextToken(lexer); + // parse function contents + final StringBuilder urlSb = new StringBuilder(); + while(true) { + if((currentToken != null) && (currentToken.getType() != CSSLexer.RPAREN) && + (currentToken.getType() != Token.EOF)) { + urlSb.append(currentToken.getText()); + } else { + break; + } + currentToken = nextToken(lexer); + } + int start = 0, end = urlSb.length(); + if (urlSb.charAt(start) == '\'' || urlSb.charAt(start) == '\"') start ++; + if (urlSb.charAt(end-1) == '\'' || urlSb.charAt(end-1) == '\"') end --; + final String url = urlSb.substring(start,end); + + // consume the format() function token + currentToken = nextToken(lexer); + final StringBuilder formatSb = new StringBuilder(); + currentToken = nextToken(lexer); + while(true) { + if((currentToken != null) && (currentToken.getType() != CSSLexer.RPAREN) && + (currentToken.getType() != Token.EOF)) { + formatSb.append(currentToken.getText()); + } else { + break; + } + currentToken = nextToken(lexer); + } + start = 0; end = formatSb.length(); + if (formatSb.charAt(start) == '\'' || formatSb.charAt(start) == '\"') start ++; + if (formatSb.charAt(end-1) == '\'' || formatSb.charAt(end-1) == '\"') end --; + final String format = formatSb.substring(start,end); + + sources.add(new FontFace.FontFaceSrc(FontFace.FontFaceSrcType.URL,url, format)); + + } else if ("local(".equalsIgnoreCase(currentToken.getText())) { + // consume the function token + currentToken = nextToken(lexer); + // parse function contents + final StringBuilder localSb = new StringBuilder(); + while(true) { + if((currentToken != null) && (currentToken.getType() != CSSLexer.RPAREN) && + (currentToken.getType() != Token.EOF)) { + localSb.append(currentToken.getText()); + } else { + break; + } + currentToken = nextToken(lexer); + } + int start = 0, end = localSb.length(); + if (localSb.charAt(start) == '\'' || localSb.charAt(start) == '\"') start ++; + if (localSb.charAt(end-1) == '\'' || localSb.charAt(end-1) == '\"') end --; + final String local = localSb.substring(start,end); + sources.add(new FontFace.FontFaceSrc(FontFace.FontFaceSrcType.LOCAL,local)); + } else { + // error unknown fontface src type + final int line = currentToken.getLine(); + final int pos = currentToken.getOffset(); + final String msg = MessageFormat.format("Unknown @font-face src type ["+currentToken.getText()+")] at [{0,number,#},{1,number,#}]",line,pos); + CssError error = createError(msg); + if (LOGGER.isLoggable(PlatformLogger.WARNING)) { + LOGGER.warning(error.toString()); + } + reportError(error); + + } + } else if (currentToken.getType() == CSSLexer.COMMA) { + // ignore + } else { + // error unexpected token + final int line = currentToken.getLine(); + final int pos = currentToken.getOffset(); + final String msg = MessageFormat.format("Unexpected TOKEN ["+currentToken.getText()+"] at [{0,number,#},{1,number,#}]",line,pos); + CssError error = createError(msg); + if (LOGGER.isLoggable(PlatformLogger.WARNING)) { + LOGGER.warning(error.toString()); + } + reportError(error); + } + } else { + break; + } + currentToken = nextToken(lexer); + } + } else { + StringBuilder descriptorVal = new StringBuilder(); + while(true) { + if((currentToken != null) && (currentToken.getType() != CSSLexer.SEMI) && + (currentToken.getType() != Token.EOF)) { + descriptorVal.append(currentToken.getText()); + } else { + break; + } + currentToken = nextToken(lexer); + } + descriptors.put(key,descriptorVal.toString()); + } + continue; + } + + if ((currentToken != null) && + (currentToken.getType() != CSSLexer.RBRACE) && + (currentToken.getType() != Token.EOF)) { + continue; + } + + // currentToken was either null or not a comma + // so we are done with selectors. + break; + } + return new FontFace(descriptors, sources); + } + + private List selectors(CSSLexer lexer) { List selectors = new ArrayList();