001/*
002 * $Id: Markup.java 4672 2011-01-30 10:38:33Z 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;
045
046import java.util.Properties;
047import java.util.StringTokenizer;
048import java.util.HashMap;
049import com.itextpdf.text.BaseColor;
050import com.itextpdf.text.Element;
051
052/**
053 * A class that contains some utilities to parse HTML attributes and content.
054 * @since 5.0.6 (some of these methods used to be in the Markup class)
055 */
056
057public class HtmlUtilities {
058
059        /**
060         * a default value for font-size 
061     * @since 2.1.3
062     */
063        public static final float DEFAULT_FONT_SIZE = 12f;
064
065    private static HashMap<String,Float> sizes = new HashMap<String,Float>();
066    static {
067        sizes.put("xx-small", new Float(4));
068        sizes.put("x-small", new Float(6));
069        sizes.put("small", new Float(8));
070        sizes.put("medium", new Float(10));
071        sizes.put("large", new Float(13));
072        sizes.put("x-large", new Float(18));
073        sizes.put("xx-large", new Float(26));
074    }
075
076        /**
077         * Parses a length.
078         * 
079         * @param string
080         *            a length in the form of an optional + or -, followed by a
081         *            number and a unit.
082         * @return a float
083         */
084
085        public static float parseLength(String string) {
086                return parseLength(string, DEFAULT_FONT_SIZE);
087        }
088
089        /**
090         * New method contributed by: Lubos Strapko
091         * 
092         * @since 2.1.3
093         */
094        public static float parseLength(String string, float actualFontSize) {
095                if (string == null)
096                        return 0f;
097        Float fl = sizes.get(string.toLowerCase());
098        if (fl != null)
099            return fl.floatValue();
100                int pos = 0;
101                int length = string.length();
102                boolean ok = true;
103                while (ok && pos < length) {
104                        switch (string.charAt(pos)) {
105                        case '+':
106                        case '-':
107                        case '0':
108                        case '1':
109                        case '2':
110                        case '3':
111                        case '4':
112                        case '5':
113                        case '6':
114                        case '7':
115                        case '8':
116                        case '9':
117                        case '.':
118                                pos++;
119                                break;
120                        default:
121                                ok = false;
122                        }
123                }
124                if (pos == 0)
125                        return 0f;
126                if (pos == length)
127                        return Float.parseFloat(string + "f");
128                float f = Float.parseFloat(string.substring(0, pos) + "f");
129                string = string.substring(pos);
130                // inches
131                if (string.startsWith("in")) {
132                        return f * 72f;
133                }
134                // centimeters
135                if (string.startsWith("cm")) {
136                        return (f / 2.54f) * 72f;
137                }
138                // millimeters
139                if (string.startsWith("mm")) {
140                        return (f / 25.4f) * 72f;
141                }
142                // picas
143                if (string.startsWith("pc")) {
144                        return f * 12f;
145                }
146                // 1em is equal to the current font size
147                if (string.startsWith("em")) {
148                        return f * actualFontSize;
149                }
150                // one ex is the x-height of a font (x-height is usually about half the
151                // font-size)
152                if (string.startsWith("ex")) {
153                        return f * actualFontSize / 2;
154                }
155                // default: we assume the length was measured in points
156                return f;
157        }
158
159        /**
160         * Converts a <CODE>BaseColor</CODE> into a HTML representation of this <CODE>
161         * BaseColor</CODE>.
162         * 
163         * @param s
164         *            the <CODE>BaseColor</CODE> that has to be converted.
165         * @return the HTML representation of this <COLOR>BaseColor </COLOR>
166         */
167
168        public static BaseColor decodeColor(String s) {
169                if (s == null)
170                        return null;
171                s = s.toLowerCase().trim();
172                try {
173                        return WebColors.getRGBColor(s);
174                }
175                catch(IllegalArgumentException iae) {
176                        return null;
177                }
178        }
179
180        /**
181         * This method parses a String with attributes and returns a Properties
182         * object.
183         * 
184         * @param string
185         *            a String of this form: 'key1="value1"; key2="value2";...
186         *            keyN="valueN" '
187         * @return a Properties object
188         */
189        public static Properties parseAttributes(String string) {
190                Properties result = new Properties();
191                if (string == null)
192                        return result;
193                StringTokenizer keyValuePairs = new StringTokenizer(string, ";");
194                StringTokenizer keyValuePair;
195                String key;
196                String value;
197                while (keyValuePairs.hasMoreTokens()) {
198                        keyValuePair = new StringTokenizer(keyValuePairs.nextToken(), ":");
199                        if (keyValuePair.hasMoreTokens())
200                                key = keyValuePair.nextToken().trim();
201                        else
202                                continue;
203                        if (keyValuePair.hasMoreTokens())
204                                value = keyValuePair.nextToken().trim();
205                        else
206                                continue;
207                        if (value.startsWith("\""))
208                                value = value.substring(1);
209                        if (value.endsWith("\""))
210                                value = value.substring(0, value.length() - 1);
211                        result.setProperty(key.toLowerCase(), value);
212                }
213                return result;
214        }
215
216        /**
217         * Removes the comments sections of a String.
218         * 
219         * @param string
220         *            the original String
221         * @param startComment
222         *            the String that marks the start of a Comment section
223         * @param endComment
224         *            the String that marks the end of a Comment section.
225         * @return the String stripped of its comment section
226         */
227        public static String removeComment(String string, String startComment,
228                        String endComment) {
229                StringBuffer result = new StringBuffer();
230                int pos = 0;
231                int end = endComment.length();
232                int start = string.indexOf(startComment, pos);
233                while (start > -1) {
234                        result.append(string.substring(pos, start));
235                        pos = string.indexOf(endComment, start) + end;
236                        start = string.indexOf(startComment, pos);
237                }
238                result.append(string.substring(pos));
239                return result.toString();
240        }
241        
242        /**
243         * Helper class that reduces the white space in a String
244         * @param content content containing whitespace
245         * @return the content without all unnecessary whitespace
246         */
247        public static String eliminateWhiteSpace(String content) {
248                // multiple spaces are reduced to one,
249                // newlines are treated as spaces,
250                // tabs, carriage returns are ignored.
251                StringBuffer buf = new StringBuffer();
252                int len = content.length();
253                char character;
254                boolean newline = false;
255                for (int i = 0; i < len; i++) {
256                        switch (character = content.charAt(i)) {
257                        case ' ':
258                                if (!newline) {
259                                        buf.append(character);
260                                }
261                                break;
262                        case '\n':
263                                if (i > 0) {
264                                        newline = true;
265                                        buf.append(' ');
266                                }
267                                break;
268                        case '\r':
269                                break;
270                        case '\t':
271                                break;
272                        default:
273                                newline = false;
274                                buf.append(character);
275                        }
276                }
277                return buf.toString();
278        }
279
280        /**
281         * A series of predefined font sizes.
282         * @since 5.0.6 (renamed)
283         */
284        public final static int FONTSIZES[] = { 8, 10, 12, 14, 18, 24, 36 };
285        
286        /**
287         * Picks a font size from a series of predefined font sizes.
288         * @param value         the new value of a font, expressed as an index
289         * @param previous      the previous value of the font size
290         * @return      a new font size.
291         */
292        public static int getIndexedFontSize(String value, String previous) {
293                // the font is expressed as an index in a series of predefined font sizes
294                int sIndex = 0;
295                // the font is defined as a relative size
296                if (value.startsWith("+") || value.startsWith("-")) {
297                        // fetch the previous value
298                        if (previous == null)
299                                previous = "12";
300                        int c = (int)Float.parseFloat(previous);
301                        // look for the nearest font size in the predefined series
302                        for (int k = FONTSIZES.length - 1; k >= 0; --k) {
303                                if (c >= FONTSIZES[k]) {
304                                        sIndex = k;
305                                        break;
306                                }
307                        }
308                        // retrieve the difference
309                        int diff =
310                                Integer.parseInt(value.startsWith("+") ?
311                                        value.substring(1) : value);
312                        // apply the difference
313                        sIndex += diff;
314                }
315                // the font is defined as an index
316                else {
317                        try {
318                                sIndex = Integer.parseInt(value) - 1;
319                        } catch (NumberFormatException nfe) {
320                                sIndex = 0;
321                        }
322                }
323                if (sIndex < 0)
324                        sIndex = 0;
325                else if (sIndex >= FONTSIZES.length)
326                        sIndex = FONTSIZES.length - 1;
327                return FONTSIZES[sIndex];
328        }
329
330        /**
331         * Translates a String value to an alignment value.
332         * (written by Norman Richards, integrated into iText by Bruno)
333         * @param       alignment a String (one of the ALIGN_ constants of this class)
334         * @return      an alignment value (one of the ALIGN_ constants of the Element interface) 
335         */
336        public static int alignmentValue(String alignment) {
337                if (alignment == null) return Element.ALIGN_UNDEFINED;
338            if (HtmlTags.ALIGN_CENTER.equalsIgnoreCase(alignment)) {
339                return Element.ALIGN_CENTER;
340            }
341            if (HtmlTags.ALIGN_LEFT.equalsIgnoreCase(alignment)) {
342                return Element.ALIGN_LEFT;
343            }
344            if (HtmlTags.ALIGN_RIGHT.equalsIgnoreCase(alignment)) {
345                return Element.ALIGN_RIGHT;
346            }
347            if (HtmlTags.ALIGN_JUSTIFY.equalsIgnoreCase(alignment)) {
348                return Element.ALIGN_JUSTIFIED;
349            }
350            if (HtmlTags.ALIGN_JUSTIFIED_ALL.equalsIgnoreCase(alignment)) {
351                return Element.ALIGN_JUSTIFIED_ALL;
352            }
353            if (HtmlTags.ALIGN_TOP.equalsIgnoreCase(alignment)) {
354                return Element.ALIGN_TOP;
355            }
356            if (HtmlTags.ALIGN_MIDDLE.equalsIgnoreCase(alignment)) {
357                return Element.ALIGN_MIDDLE;
358            }
359            if (HtmlTags.ALIGN_BOTTOM.equalsIgnoreCase(alignment)) {
360                return Element.ALIGN_BOTTOM;
361            }
362            if (HtmlTags.ALIGN_BASELINE.equalsIgnoreCase(alignment)) {
363                return Element.ALIGN_BASELINE;
364            }
365            return Element.ALIGN_UNDEFINED;
366        }
367}