001/* 002 * $Id: StyleSheet.java 4784 2011-03-15 08:33:00Z blowagie $ 003 * 004 * This file is part of the iText (R) project. 005 * Copyright (c) 1998-2011 1T3XT BVBA 006 * Authors: Bruno Lowagie, Paulo Soares, et al. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU Affero General Public License version 3 010 * as published by the Free Software Foundation with the addition of the 011 * following permission added to Section 15 as permitted in Section 7(a): 012 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT, 013 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS. 014 * 015 * This program is distributed in the hope that it will be useful, but 016 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 017 * or FITNESS FOR A PARTICULAR PURPOSE. 018 * See the GNU Affero General Public License for more details. 019 * You should have received a copy of the GNU Affero General Public License 020 * along with this program; if not, see http://www.gnu.org/licenses or write to 021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 022 * Boston, MA, 02110-1301 USA, or download the license from the following URL: 023 * http://itextpdf.com/terms-of-use/ 024 * 025 * The interactive user interfaces in modified source and object code versions 026 * of this program must display Appropriate Legal Notices, as required under 027 * Section 5 of the GNU Affero General Public License. 028 * 029 * In accordance with Section 7(b) of the GNU Affero General Public License, 030 * a covered work must retain the producer line in every PDF that is created 031 * or manipulated using iText. 032 * 033 * You can be released from the requirements of the license by purchasing 034 * a commercial license. Buying such a license is mandatory as soon as you 035 * develop commercial activities involving the iText software without 036 * disclosing the source code of your own applications. 037 * These activities include: offering paid services to customers as an ASP, 038 * serving PDFs on the fly in a web application, shipping iText with a closed 039 * source product. 040 * 041 * For more information, please contact iText Software Corp. at this 042 * address: sales@itextpdf.com 043 */ 044package com.itextpdf.text.html.simpleparser; 045 046import java.util.HashMap; 047import java.util.Map; 048import java.util.Properties; 049 050import com.itextpdf.text.BaseColor; 051import com.itextpdf.text.html.HtmlTags; 052import com.itextpdf.text.html.HtmlUtilities; 053 054public class StyleSheet { 055 056 /** 057 * Map storing tags and their corresponding styles. 058 * @since 5.0.6 (changed HashMap => Map) 059 */ 060 protected Map<String, Map<String, String>> tagMap = new HashMap<String, Map<String, String>>(); 061 062 /** 063 * Map storing possible names of the "class" attribute 064 * and their corresponding styles. 065 * @since 5.0.6 (changed HashMap => Map) 066 */ 067 protected Map<String, Map<String, String>> classMap = new HashMap<String, Map<String, String>>(); 068 069 /** 070 * Creates a new instance of StyleSheet 071 */ 072 public StyleSheet() { 073 } 074 075 /** 076 * Associates a Map containing styles with a tag. 077 * @param tag the name of the HTML/XML tag 078 * @param attrs a map containing styles 079 */ 080 public void loadTagStyle(String tag, Map<String, String> attrs) { 081 tagMap.put(tag.toLowerCase(), attrs); 082 } 083 084 /** 085 * Adds an extra style key-value pair to the styles Map 086 * of a specific tag 087 * @param tag the name of the HTML/XML tag 088 * @param key the key specifying a specific style 089 * @param value the value defining the style 090 */ 091 public void loadTagStyle(String tag, String key, String value) { 092 tag = tag.toLowerCase(); 093 Map<String, String> styles = tagMap.get(tag); 094 if (styles == null) { 095 styles = new HashMap<String, String>(); 096 tagMap.put(tag, styles); 097 } 098 styles.put(key, value); 099 } 100 101 /** 102 * Associates a Map containing styles with a class name. 103 * @param className the value of the class attribute 104 * @param attrs a map containing styles 105 */ 106 public void loadStyle(String className, HashMap<String, String> attrs) { 107 classMap.put(className.toLowerCase(), attrs); 108 } 109 110 /** 111 * Adds an extra style key-value pair to the styles Map 112 * of a specific tag 113 * @param className the name of the HTML/XML tag 114 * @param key the key specifying a specific style 115 * @param value the value defining the style 116 */ 117 public void loadStyle(String className, String key, String value) { 118 className = className.toLowerCase(); 119 Map<String, String> styles = classMap.get(className); 120 if (styles == null) { 121 styles = new HashMap<String, String>(); 122 classMap.put(className, styles); 123 } 124 styles.put(key, value); 125 } 126 127 /** 128 * Resolves the styles based on the tag name and the value 129 * of the class attribute. 130 * @param tag the tag that needs to be resolved 131 * @param attrs existing style map that will be updated 132 */ 133 public void applyStyle(String tag, Map<String, String> attrs) { 134 // first fetch the styles corresponding with the tag name 135 Map<String, String> map = tagMap.get(tag.toLowerCase()); 136 if (map != null) { 137 // create a new map with properties 138 Map<String, String> temp = new HashMap<String, String>(map); 139 // override with the existing properties 140 temp.putAll(attrs); 141 // update the existing properties 142 attrs.putAll(temp); 143 } 144 // look for the class attribute 145 String cm = attrs.get(HtmlTags.CLASS); 146 if (cm == null) 147 return; 148 // fetch the styles corresponding with the class attribute 149 map = classMap.get(cm.toLowerCase()); 150 if (map == null) 151 return; 152 // remove the class attribute from the properties 153 attrs.remove(HtmlTags.CLASS); 154 // create a map with the styles corresponding with the class value 155 Map<String, String> temp = new HashMap<String, String>(map); 156 // override with the existing properties 157 temp.putAll(attrs); 158 // update the properties 159 attrs.putAll(temp); 160 } 161 162 /** 163 * Method contributed by Lubos Strapko 164 * @param h 165 * @param chain 166 * @since 2.1.3 167 */ 168 public static void resolveStyleAttribute(Map<String, String> h, ChainedProperties chain) { 169 String style = h.get(HtmlTags.STYLE); 170 if (style == null) 171 return; 172 Properties prop = HtmlUtilities.parseAttributes(style); 173 for (Object element : prop.keySet()) { 174 String key = (String) element; 175 if (key.equals(HtmlTags.FONTFAMILY)) { 176 h.put(HtmlTags.FACE, prop.getProperty(key)); 177 } else if (key.equals(HtmlTags.FONTSIZE)) { 178 float actualFontSize = HtmlUtilities.parseLength(chain 179 .getProperty(HtmlTags.SIZE), 180 HtmlUtilities.DEFAULT_FONT_SIZE); 181 if (actualFontSize <= 0f) 182 actualFontSize = HtmlUtilities.DEFAULT_FONT_SIZE; 183 h.put(HtmlTags.SIZE, Float.toString(HtmlUtilities.parseLength(prop 184 .getProperty(key), actualFontSize)) 185 + "pt"); 186 } else if (key.equals(HtmlTags.FONTSTYLE)) { 187 String ss = prop.getProperty(key).trim().toLowerCase(); 188 if (ss.equals(HtmlTags.ITALIC) || ss.equals(HtmlTags.OBLIQUE)) 189 h.put(HtmlTags.I, null); 190 } else if (key.equals(HtmlTags.FONTWEIGHT)) { 191 String ss = prop.getProperty(key).trim().toLowerCase(); 192 if (ss.equals(HtmlTags.BOLD) || ss.equals("700") || ss.equals("800") 193 || ss.equals("900")) 194 h.put(HtmlTags.B, null); 195 } else if (key.equals(HtmlTags.TEXTDECORATION)) { 196 String ss = prop.getProperty(key).trim().toLowerCase(); 197 if (ss.equals(HtmlTags.UNDERLINE)) 198 h.put(HtmlTags.U, null); 199 } else if (key.equals(HtmlTags.COLOR)) { 200 BaseColor c = HtmlUtilities.decodeColor(prop.getProperty(key)); 201 if (c != null) { 202 int hh = c.getRGB(); 203 String hs = Integer.toHexString(hh); 204 hs = "000000" + hs; 205 hs = "#" + hs.substring(hs.length() - 6); 206 h.put(HtmlTags.COLOR, hs); 207 } 208 } else if (key.equals(HtmlTags.LINEHEIGHT)) { 209 String ss = prop.getProperty(key).trim(); 210 float actualFontSize = HtmlUtilities.parseLength(chain 211 .getProperty(HtmlTags.SIZE), 212 HtmlUtilities.DEFAULT_FONT_SIZE); 213 if (actualFontSize <= 0f) 214 actualFontSize = HtmlUtilities.DEFAULT_FONT_SIZE; 215 float v = HtmlUtilities.parseLength(prop.getProperty(key), 216 actualFontSize); 217 if (ss.endsWith("%")) { 218 h.put(HtmlTags.LEADING, "0," + v / 100); 219 return; 220 } 221 if (HtmlTags.NORMAL.equalsIgnoreCase(ss)) { 222 h.put(HtmlTags.LEADING, "0,1.5"); 223 return; 224 } 225 h.put(HtmlTags.LEADING, v + ",0"); 226 } else if (key.equals(HtmlTags.TEXTALIGN)) { 227 String ss = prop.getProperty(key).trim().toLowerCase(); 228 h.put(HtmlTags.ALIGN, ss); 229 } else if (key.equals(HtmlTags.PADDINGLEFT)) { 230 String ss = prop.getProperty(key).trim().toLowerCase(); 231 h.put(HtmlTags.INDENT, Float.toString(HtmlUtilities.parseLength(ss))); 232 } 233 } 234 } 235}