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}