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}