Showing the changes to java/pkg/javax/swing/text/html/MinimalHTMLWriter.java (1 of 18352 files). Show all
Unified · Split
| @@ -0,0 +1,726 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package javax.swing.text.html; | ||
| 27 | |||
| 28 | import java.io.Writer; | ||
| 29 | import java.io.IOException; | ||
| 30 | import java.util.*; | ||
| 31 | import java.awt.Color; | ||
| 32 | import javax.swing.text.*; | ||
| 33 | |||
| 34 | /** | ||
| 35 | * MinimalHTMLWriter is a fallback writer used by the | ||
| 36 | * HTMLEditorKit to write out HTML for a document that | ||
| 37 | * is a not produced by the EditorKit. | ||
| 38 | * | ||
| 39 | * The format for the document is: | ||
| 40 | * <pre> | ||
| 41 | * <html> | ||
| 42 | * <head> | ||
| 43 | * <style> | ||
| 44 | * <!-- list of named styles | ||
| 45 | * p.normal { | ||
| 46 | * font-family: SansSerif; | ||
| 47 | * margin-height: 0; | ||
| 48 | * font-size: 14 | ||
| 49 | * } | ||
| 50 | * --> | ||
| 51 | * </style> | ||
| 52 | * </head> | ||
| 53 | * <body> | ||
| 54 | * <p style=normal> | ||
| 55 | * <b>Bold, italic, and underline attributes | ||
| 56 | * of the run are emitted as HTML tags. | ||
| 57 | * The remaining attributes are emitted as | ||
| 58 | * part of the style attribute of a <span> tag. | ||
| 59 | * The syntax is similar to inline styles.</b> | ||
| 60 | * </p> | ||
| 61 | * </body> | ||
| 62 | * </html> | ||
| 63 | * </pre> | ||
| 64 | * | ||
| 65 | * @author Sunita Mani | ||
| 66 | */ | ||
| 67 | |||
| 68 | public class MinimalHTMLWriter extends AbstractWriter { | ||
| 69 | |||
| 70 | /** | ||
| 71 | * These static finals are used to | ||
| 72 | * tweak and query the fontMask about which | ||
| 73 | * of these tags need to be generated or | ||
| 74 | * terminated. | ||
| 75 | */ | ||
| 76 | private static final int BOLD = 0x01; | ||
| 77 | private static final int ITALIC = 0x02; | ||
| 78 | private static final int UNDERLINE = 0x04; | ||
| 79 | |||
| 80 | // Used to map StyleConstants to CSS. | ||
| 81 | private static final CSS css = new CSS(); | ||
| 82 | |||
| 83 | private int fontMask = 0; | ||
| 84 | |||
| 85 | int startOffset = 0; | ||
| 86 | int endOffset = 0; | ||
| 87 | |||
| 88 | /** | ||
| 89 | * Stores the attributes of the previous run. | ||
| 90 | * Used to compare with the current run's | ||
| 91 | * attributeset. If identical, then a | ||
| 92 | * <span> tag is not emitted. | ||
| 93 | */ | ||
| 94 | private AttributeSet fontAttributes; | ||
| 95 | |||
| 96 | /** | ||
| 97 | * Maps from style name as held by the Document, to the archived | ||
| 98 | * style name (style name written out). These may differ. | ||
| 99 | */ | ||
| 100 | private Hashtable<String, String> styleNameMapping; | ||
| 101 | |||
| 102 | /** | ||
| 103 | * Creates a new MinimalHTMLWriter. | ||
| 104 | * | ||
| 105 | * @param w Writer | ||
| 106 | * @param doc StyledDocument | ||
| 107 | * | ||
| 108 | */ | ||
| 109 | public MinimalHTMLWriter(Writer w, StyledDocument doc) { | ||
| 110 | super(w, doc); | ||
| 111 | } | ||
| 112 | |||
| 113 | /** | ||
| 114 | * Creates a new MinimalHTMLWriter. | ||
| 115 | * | ||
| 116 | * @param w Writer | ||
| 117 | * @param doc StyledDocument | ||
| 118 | * @param pos The location in the document to fetch the | ||
| 119 | * content. | ||
| 120 | * @param len The amount to write out. | ||
| 121 | * | ||
| 122 | */ | ||
| 123 | public MinimalHTMLWriter(Writer w, StyledDocument doc, int pos, int len) { | ||
| 124 | super(w, doc, pos, len); | ||
| 125 | } | ||
| 126 | |||
| 127 | /** | ||
| 128 | * Generates HTML output | ||
| 129 | * from a StyledDocument. | ||
| 130 | * | ||
| 131 | * @exception IOException on any I/O error | ||
| 132 | * @exception BadLocationException if pos represents an invalid | ||
| 133 | * location within the document. | ||
| 134 | * | ||
| 135 | */ | ||
| 136 | public void write() throws IOException, BadLocationException { | ||
| 137 | styleNameMapping = new Hashtable<String, String>(); | ||
| 138 | writeStartTag("<html>"); | ||
| 139 | writeHeader(); | ||
| 140 | writeBody(); | ||
| 141 | writeEndTag("</html>"); | ||
| 142 | } | ||
| 143 | |||
| 144 | |||
| 145 | /** | ||
| 146 | * Writes out all the attributes for the | ||
| 147 | * following types: | ||
| 148 | * StyleConstants.ParagraphConstants, | ||
| 149 | * StyleConstants.CharacterConstants, | ||
| 150 | * StyleConstants.FontConstants, | ||
| 151 | * StyleConstants.ColorConstants. | ||
| 152 | * The attribute name and value are separated by a colon. | ||
| 153 | * Each pair is separated by a semicolon. | ||
| 154 | * | ||
| 155 | * @exception IOException on any I/O error | ||
| 156 | */ | ||
| 157 | protected void writeAttributes(AttributeSet attr) throws IOException { | ||
| 158 | Enumeration attributeNames = attr.getAttributeNames(); | ||
| 159 | while (attributeNames.hasMoreElements()) { | ||
| 160 | Object name = attributeNames.nextElement(); | ||
| 161 | if ((name instanceof StyleConstants.ParagraphConstants) || | ||
| 162 | (name instanceof StyleConstants.CharacterConstants) || | ||
| 163 | (name instanceof StyleConstants.FontConstants) || | ||
| 164 | (name instanceof StyleConstants.ColorConstants)) { | ||
| 165 | indent(); | ||
| 166 | write(name.toString()); | ||
| 167 | write(':'); | ||
| 168 | write(css.styleConstantsValueToCSSValue | ||
| 169 | ((StyleConstants)name, attr.getAttribute(name)). | ||
| 170 | toString()); | ||
| 171 | write(';'); | ||
| 172 | write(NEWLINE); | ||
| 173 | } | ||
| 174 | } | ||
| 175 | } | ||
| 176 | |||
| 177 | |||
| 178 | /** | ||
| 179 | * Writes out text. | ||
| 180 | * | ||
| 181 | * @exception IOException on any I/O error | ||
| 182 | */ | ||
| 183 | protected void text(Element elem) throws IOException, BadLocationException { | ||
| 184 | String contentStr = getText(elem); | ||
| 185 | if ((contentStr.length() > 0) && | ||
| 186 | (contentStr.charAt(contentStr.length()-1) == NEWLINE)) { | ||
| 187 | contentStr = contentStr.substring(0, contentStr.length()-1); | ||
| 188 | } | ||
| 189 | if (contentStr.length() > 0) { | ||
| 190 | write(contentStr); | ||
| 191 | } | ||
| 192 | } | ||
| 193 | |||
| 194 | /** | ||
| 195 | * Writes out a start tag appropriately | ||
| 196 | * indented. Also increments the indent level. | ||
| 197 | * | ||
| 198 | * @exception IOException on any I/O error | ||
| 199 | */ | ||
| 200 | protected void writeStartTag(String tag) throws IOException { | ||
| 201 | indent(); | ||
| 202 | write(tag); | ||
| 203 | write(NEWLINE); | ||
| 204 | incrIndent(); | ||
| 205 | } | ||
| 206 | |||
| 207 | |||
| 208 | /** | ||
| 209 | * Writes out an end tag appropriately | ||
| 210 | * indented. Also decrements the indent level. | ||
| 211 | * | ||
| 212 | * @exception IOException on any I/O error | ||
| 213 | */ | ||
| 214 | protected void writeEndTag(String endTag) throws IOException { | ||
| 215 | decrIndent(); | ||
| 216 | indent(); | ||
| 217 | write(endTag); | ||
| 218 | write(NEWLINE); | ||
| 219 | } | ||
| 220 | |||
| 221 | |||
| 222 | /** | ||
| 223 | * Writes out the <head> and <style> | ||
| 224 | * tags, and then invokes writeStyles() to write | ||
| 225 | * out all the named styles as the content of the | ||
| 226 | * <style> tag. The content is surrounded by | ||
| 227 | * valid HTML comment markers to ensure that the | ||
| 228 | * document is viewable in applications/browsers | ||
| 229 | * that do not support the tag. | ||
| 230 | * | ||
| 231 | * @exception IOException on any I/O error | ||
| 232 | */ | ||
| 233 | protected void writeHeader() throws IOException { | ||
| 234 | writeStartTag("<head>"); | ||
| 235 | writeStartTag("<style>"); | ||
| 236 | writeStartTag("<!--"); | ||
| 237 | writeStyles(); | ||
| 238 | writeEndTag("-->"); | ||
| 239 | writeEndTag("</style>"); | ||
| 240 | writeEndTag("</head>"); | ||
| 241 | } | ||
| 242 | |||
| 243 | |||
| 244 | |||
| 245 | /** | ||
| 246 | * Writes out all the named styles as the | ||
| 247 | * content of the <style> tag. | ||
| 248 | * | ||
| 249 | * @exception IOException on any I/O error | ||
| 250 | */ | ||
| 251 | protected void writeStyles() throws IOException { | ||
| 252 | /* | ||
| 253 | * Access to DefaultStyledDocument done to workaround | ||
| 254 | * a missing API in styled document to access the | ||
| 255 | * stylenames. | ||
| 256 | */ | ||
| 257 | DefaultStyledDocument styledDoc = ((DefaultStyledDocument)getDocument()); | ||
| 258 | Enumeration styleNames = styledDoc.getStyleNames(); | ||
| 259 | |||
| 260 | while (styleNames.hasMoreElements()) { | ||
| 261 | Style s = styledDoc.getStyle((String)styleNames.nextElement()); | ||
| 262 | |||
| 263 | /** PENDING: Once the name attribute is removed | ||
| 264 | from the list we check check for 0. **/ | ||
| 265 | if (s.getAttributeCount() == 1 && | ||
| 266 | s.isDefined(StyleConstants.NameAttribute)) { | ||
| 267 | continue; | ||
| 268 | } | ||
| 269 | indent(); | ||
| 270 | write("p." + addStyleName(s.getName())); | ||
| 271 | write(" {\n"); | ||
| 272 | incrIndent(); | ||
| 273 | writeAttributes(s); | ||
| 274 | decrIndent(); | ||
| 275 | indent(); | ||
| 276 | write("}\n"); | ||
| 277 | } | ||
| 278 | } | ||
| 279 | |||
| 280 | |||
| 281 | /** | ||
| 282 | * Iterates over the elements in the document | ||
| 283 | * and processes elements based on whether they are | ||
| 284 | * branch elements or leaf elements. This method specially handles | ||
| 285 | * leaf elements that are text. | ||
| 286 | * | ||
| 287 | * @exception IOException on any I/O error | ||
| 288 | */ | ||
| 289 | protected void writeBody() throws IOException, BadLocationException { | ||
| 290 | ElementIterator it = getElementIterator(); | ||
| 291 | |||
| 292 | /* | ||
| 293 | This will be a section element for a styled document. | ||
| 294 | We represent this element in HTML as the body tags. | ||
| 295 | Therefore we ignore it. | ||
| 296 | */ | ||
| 297 | it.current(); | ||
| 298 | |||
| 299 | Element next; | ||
| 300 | |||
| 301 | writeStartTag("<body>"); | ||
| 302 | |||
| 303 | boolean inContent = false; | ||
| 304 | |||
| 305 | while((next = it.next()) != null) { | ||
| 306 | if (!inRange(next)) { | ||
| 307 | continue; | ||
| 308 | } | ||
| 309 | if (next instanceof AbstractDocument.BranchElement) { | ||
| 310 | if (inContent) { | ||
| 311 | writeEndParagraph(); | ||
| 312 | inContent = false; | ||
| 313 | fontMask = 0; | ||
| 314 | } | ||
| 315 | writeStartParagraph(next); | ||
| 316 | } else if (isText(next)) { | ||
| 317 | writeContent(next, !inContent); | ||
| 318 | inContent = true; | ||
| 319 | } else { | ||
| 320 | writeLeaf(next); | ||
| 321 | inContent = true; | ||
| 322 | } | ||
| 323 | } | ||
| 324 | if (inContent) { | ||
| 325 | writeEndParagraph(); | ||
| 326 | } | ||
| 327 | writeEndTag("</body>"); | ||
| 328 | } | ||
| 329 | |||
| 330 | |||
| 331 | /** | ||
| 332 | * Emits an end tag for a <p> | ||
| 333 | * tag. Before writing out the tag, this method ensures | ||
| 334 | * that all other tags that have been opened are | ||
| 335 | * appropriately closed off. | ||
| 336 | * | ||
| 337 | * @exception IOException on any I/O error | ||
| 338 | */ | ||
| 339 | protected void writeEndParagraph() throws IOException { | ||
| 340 | writeEndMask(fontMask); | ||
| 341 | if (inFontTag()) { | ||
| 342 | endSpanTag(); | ||
| 343 | } else { | ||
| 344 | write(NEWLINE); | ||
| 345 | } | ||
| 346 | writeEndTag("</p>"); | ||
| 347 | } | ||
| 348 | |||
| 349 | |||
| 350 | /** | ||
| 351 | * Emits the start tag for a paragraph. If | ||
| 352 | * the paragraph has a named style associated with it, | ||
| 353 | * then this method also generates a class attribute for the | ||
| 354 | * <p> tag and sets its value to be the name of the | ||
| 355 | * style. | ||
| 356 | * | ||
| 357 | * @exception IOException on any I/O error | ||
| 358 | */ | ||
| 359 | protected void writeStartParagraph(Element elem) throws IOException { | ||
| 360 | AttributeSet attr = elem.getAttributes(); | ||
| 361 | Object resolveAttr = attr.getAttribute(StyleConstants.ResolveAttribute); | ||
| 362 | if (resolveAttr instanceof StyleContext.NamedStyle) { | ||
| 363 | writeStartTag("<p class=" + mapStyleName(((StyleContext.NamedStyle)resolveAttr).getName()) + ">"); | ||
| 364 | } else { | ||
| 365 | writeStartTag("<p>"); | ||
| 366 | } | ||
| 367 | } | ||
| 368 | |||
| 369 | |||
| 370 | /** | ||
| 371 | * Responsible for writing out other non-text leaf | ||
| 372 | * elements. | ||
| 373 | * | ||
| 374 | * @exception IOException on any I/O error | ||
| 375 | */ | ||
| 376 | protected void writeLeaf(Element elem) throws IOException { | ||
| 377 | indent(); | ||
| 378 | if (elem.getName() == StyleConstants.IconElementName) { | ||
| 379 | writeImage(elem); | ||
| 380 | } else if (elem.getName() == StyleConstants.ComponentElementName) { | ||
| 381 | writeComponent(elem); | ||
| 382 | } | ||
| 383 | } | ||
| 384 | |||
| 385 | |||
| 386 | /** | ||
| 387 | * Responsible for handling Icon Elements; | ||
| 388 | * deliberately unimplemented. How to implement this method is | ||
| 389 | * an issue of policy. For example, if you're generating | ||
| 390 | * an <img> tag, how should you | ||
| 391 | * represent the src attribute (the location of the image)? | ||
| 392 | * In certain cases it could be a URL, in others it could | ||
| 393 | * be read from a stream. | ||
| 394 | * | ||
| 395 | * @param elem element of type StyleConstants.IconElementName | ||
| 396 | */ | ||
| 397 | protected void writeImage(Element elem) throws IOException { | ||
| 398 | } | ||
| 399 | |||
| 400 | |||
| 401 | /** | ||
| 402 | * Responsible for handling Component Elements; | ||
| 403 | * deliberately unimplemented. | ||
| 404 | * How this method is implemented is a matter of policy. | ||
| 405 | */ | ||
| 406 | protected void writeComponent(Element elem) throws IOException { | ||
| 407 | } | ||
| 408 | |||
| 409 | |||
| 410 | /** | ||
| 411 | * Returns true if the element is a text element. | ||
| 412 | * | ||
| 413 | */ | ||
| 414 | protected boolean isText(Element elem) { | ||
| 415 | return (elem.getName() == AbstractDocument.ContentElementName); | ||
| 416 | } | ||
| 417 | |||
| 418 | |||
| 419 | /** | ||
| 420 | * Writes out the attribute set | ||
| 421 | * in an HTML-compliant manner. | ||
| 422 | * | ||
| 423 | * @exception IOException on any I/O error | ||
| 424 | * @exception BadLocationException if pos represents an invalid | ||
| 425 | * location within the document. | ||
| 426 | */ | ||
| 427 | protected void writeContent(Element elem, boolean needsIndenting) | ||
| 428 | throws IOException, BadLocationException { | ||
| 429 | |||
| 430 | AttributeSet attr = elem.getAttributes(); | ||
| 431 | writeNonHTMLAttributes(attr); | ||
| 432 | if (needsIndenting) { | ||
| 433 | indent(); | ||
| 434 | } | ||
| 435 | writeHTMLTags(attr); | ||
| 436 | text(elem); | ||
| 437 | } | ||
| 438 | |||
| 439 | |||
| 440 | /** | ||
| 441 | * Generates | ||
| 442 | * bold <b>, italic <i>, and <u> tags for the | ||
| 443 | * text based on its attribute settings. | ||
| 444 | * | ||
| 445 | * @exception IOException on any I/O error | ||
| 446 | */ | ||
| 447 | |||
| 448 | protected void writeHTMLTags(AttributeSet attr) throws IOException { | ||
| 449 | |||
| 450 | int oldMask = fontMask; | ||
| 451 | setFontMask(attr); | ||
| 452 | |||
| 453 | int endMask = 0; | ||
| 454 | int startMask = 0; | ||
| 455 | if ((oldMask & BOLD) != 0) { | ||
| 456 | if ((fontMask & BOLD) == 0) { | ||
| 457 | endMask |= BOLD; | ||
| 458 | } | ||
| 459 | } else if ((fontMask & BOLD) != 0) { | ||
| 460 | startMask |= BOLD; | ||
| 461 | } | ||
| 462 | |||
| 463 | if ((oldMask & ITALIC) != 0) { | ||
| 464 | if ((fontMask & ITALIC) == 0) { | ||
| 465 | endMask |= ITALIC; | ||
| 466 | } | ||
| 467 | } else if ((fontMask & ITALIC) != 0) { | ||
| 468 | startMask |= ITALIC; | ||
| 469 | } | ||
| 470 | |||
| 471 | if ((oldMask & UNDERLINE) != 0) { | ||
| 472 | if ((fontMask & UNDERLINE) == 0) { | ||
| 473 | endMask |= UNDERLINE; | ||
| 474 | } | ||
| 475 | } else if ((fontMask & UNDERLINE) != 0) { | ||
| 476 | startMask |= UNDERLINE; | ||
| 477 | } | ||
| 478 | writeEndMask(endMask); | ||
| 479 | writeStartMask(startMask); | ||
| 480 | } | ||
| 481 | |||
| 482 | |||
| 483 | /** | ||
| 484 | * Tweaks the appropriate bits of fontMask | ||
| 485 | * to reflect whether the text is to be displayed in | ||
| 486 | * bold, italic, and/or with an underline. | ||
| 487 | * | ||
| 488 | */ | ||
| 489 | private void setFontMask(AttributeSet attr) { | ||
| 490 | if (StyleConstants.isBold(attr)) { | ||
| 491 | fontMask |= BOLD; | ||
| 492 | } | ||
| 493 | |||
| 494 | if (StyleConstants.isItalic(attr)) { | ||
| 495 | fontMask |= ITALIC; | ||
| 496 | } | ||
| 497 | |||
| 498 | if (StyleConstants.isUnderline(attr)) { | ||
| 499 | fontMask |= UNDERLINE; | ||
| 500 | } | ||
| 501 | } | ||
| 502 | |||
| 503 | |||
| 504 | |||
| 505 | |||
| 506 | /** | ||
| 507 | * Writes out start tags <u>, <i>, and <b> based on | ||
| 508 | * the mask settings. | ||
| 509 | * | ||
| 510 | * @exception IOException on any I/O error | ||
| 511 | */ | ||
| 512 | private void writeStartMask(int mask) throws IOException { | ||
| 513 | if (mask != 0) { | ||
| 514 | if ((mask & UNDERLINE) != 0) { | ||
| 515 | write("<u>"); | ||
| 516 | } | ||
| 517 | if ((mask & ITALIC) != 0) { | ||
| 518 | write("<i>"); | ||
| 519 | } | ||
| 520 | if ((mask & BOLD) != 0) { | ||
| 521 | write("<b>"); | ||
| 522 | } | ||
| 523 | } | ||
| 524 | } | ||
| 525 | |||
| 526 | /** | ||
| 527 | * Writes out end tags for <u>, <i>, and <b> based on | ||
| 528 | * the mask settings. | ||
| 529 | * | ||
| 530 | * @exception IOException on any I/O error | ||
| 531 | */ | ||
| 532 | private void writeEndMask(int mask) throws IOException { | ||
| 533 | if (mask != 0) { | ||
| 534 | if ((mask & BOLD) != 0) { | ||
| 535 | write("</b>"); | ||
| 536 | } | ||
| 537 | if ((mask & ITALIC) != 0) { | ||
| 538 | write("</i>"); | ||
| 539 | } | ||
| 540 | if ((mask & UNDERLINE) != 0) { | ||
| 541 | write("</u>"); | ||
| 542 | } | ||
| 543 | } | ||
| 544 | } | ||
| 545 | |||
| 546 | |||
| 547 | /** | ||
| 548 | * Writes out the remaining | ||
| 549 | * character-level attributes (attributes other than bold, | ||
| 550 | * italic, and underline) in an HTML-compliant way. Given that | ||
| 551 | * attributes such as font family and font size have no direct | ||
| 552 | * mapping to HTML tags, a <span> tag is generated and its | ||
| 553 | * style attribute is set to contain the list of remaining | ||
| 554 | * attributes just like inline styles. | ||
| 555 | * | ||
| 556 | * @exception IOException on any I/O error | ||
| 557 | */ | ||
| 558 | protected void writeNonHTMLAttributes(AttributeSet attr) throws IOException { | ||
| 559 | |||
| 560 | String style = ""; | ||
| 561 | String separator = "; "; | ||
| 562 | |||
| 563 | if (inFontTag() && fontAttributes.isEqual(attr)) { | ||
| 564 | return; | ||
| 565 | } | ||
| 566 | |||
| 567 | boolean first = true; | ||
| 568 | Color color = (Color)attr.getAttribute(StyleConstants.Foreground); | ||
| 569 | if (color != null) { | ||
| 570 | style += "color: " + css.styleConstantsValueToCSSValue | ||
| 571 | ((StyleConstants)StyleConstants.Foreground, | ||
| 572 | color); | ||
| 573 | first = false; | ||
| 574 | } | ||
| 575 | Integer size = (Integer)attr.getAttribute(StyleConstants.FontSize); | ||
| 576 | if (size != null) { | ||
| 577 | if (!first) { | ||
| 578 | style += separator; | ||
| 579 | } | ||
| 580 | style += "font-size: " + size.intValue() + "pt"; | ||
| 581 | first = false; | ||
| 582 | } | ||
| 583 | |||
| 584 | String family = (String)attr.getAttribute(StyleConstants.FontFamily); | ||
| 585 | if (family != null) { | ||
| 586 | if (!first) { | ||
| 587 | style += separator; | ||
| 588 | } | ||
| 589 | style += "font-family: " + family; | ||
| 590 | first = false; | ||
| 591 | } | ||
| 592 | |||
| 593 | if (style.length() > 0) { | ||
| 594 | if (fontMask != 0) { | ||
| 595 | writeEndMask(fontMask); | ||
| 596 | fontMask = 0; | ||
| 597 | } | ||
| 598 | startSpanTag(style); | ||
| 599 | fontAttributes = attr; | ||
| 600 | } | ||
| 601 | else if (fontAttributes != null) { | ||
| 602 | writeEndMask(fontMask); | ||
| 603 | fontMask = 0; | ||
| 604 | endSpanTag(); | ||
| 605 | } | ||
| 606 | } | ||
| 607 | |||
| 608 | |||
| 609 | /** | ||
| 610 | * Returns true if we are currently in a <font> tag. | ||
| 611 | */ | ||
| 612 | protected boolean inFontTag() { | ||
| 613 | return (fontAttributes != null); | ||
| 614 | } | ||
| 615 | |||
| 616 | /** | ||
| 617 | * This is no longer used, instead <span> will be written out. | ||
| 618 | * <p> | ||
| 619 | * Writes out an end tag for the <font> tag. | ||
| 620 | * | ||
| 621 | * @exception IOException on any I/O error | ||
| 622 | */ | ||
| 623 | protected void endFontTag() throws IOException { | ||
| 624 | write(NEWLINE); | ||
| 625 | writeEndTag("</font>"); | ||
| 626 | fontAttributes = null; | ||
| 627 | } | ||
| 628 | |||
| 629 | |||
| 630 | /** | ||
| 631 | * This is no longer used, instead <span> will be written out. | ||
| 632 | * <p> | ||
| 633 | * Writes out a start tag for the <font> tag. | ||
| 634 | * Because font tags cannot be nested, | ||
| 635 | * this method closes out | ||
| 636 | * any enclosing font tag before writing out a | ||
| 637 | * new start tag. | ||
| 638 | * | ||
| 639 | * @exception IOException on any I/O error | ||
| 640 | */ | ||
| 641 | protected void startFontTag(String style) throws IOException { | ||
| 642 | boolean callIndent = false; | ||
| 643 | if (inFontTag()) { | ||
| 644 | endFontTag(); | ||
| 645 | callIndent = true; | ||
| 646 | } | ||
| 647 | writeStartTag("<font style=\"" + style + "\">"); | ||
| 648 | if (callIndent) { | ||
| 649 | indent(); | ||
| 650 | } | ||
| 651 | } | ||
| 652 | |||
| 653 | /** | ||
| 654 | * Writes out a start tag for the <font> tag. | ||
| 655 | * Because font tags cannot be nested, | ||
| 656 | * this method closes out | ||
| 657 | * any enclosing font tag before writing out a | ||
| 658 | * new start tag. | ||
| 659 | * | ||
| 660 | * @exception IOException on any I/O error | ||
| 661 | */ | ||
| 662 | private void startSpanTag(String style) throws IOException { | ||
| 663 | boolean callIndent = false; | ||
| 664 | if (inFontTag()) { | ||
| 665 | endSpanTag(); | ||
| 666 | callIndent = true; | ||
| 667 | } | ||
| 668 | writeStartTag("<span style=\"" + style + "\">"); | ||
| 669 | if (callIndent) { | ||
| 670 | indent(); | ||
| 671 | } | ||
| 672 | } | ||
| 673 | |||
| 674 | /** | ||
| 675 | * Writes out an end tag for the <span> tag. | ||
| 676 | * | ||
| 677 | * @exception IOException on any I/O error | ||
| 678 | */ | ||
| 679 | private void endSpanTag() throws IOException { | ||
| 680 | write(NEWLINE); | ||
| 681 | writeEndTag("</span>"); | ||
| 682 | fontAttributes = null; | ||
| 683 | } | ||
| 684 | |||
| 685 | /** | ||
| 686 | * Adds the style named <code>style</code> to the style mapping. This | ||
| 687 | * returns the name that should be used when outputting. CSS does not | ||
| 688 | * allow the full Unicode set to be used as a style name. | ||
| 689 | */ | ||
| 690 | private String addStyleName(String style) { | ||
| 691 | if (styleNameMapping == null) { | ||
| 692 | return style; | ||
| 693 | } | ||
| 694 | StringBuilder sb = null; | ||
| 695 | for (int counter = style.length() - 1; counter >= 0; counter--) { | ||
| 696 | if (!isValidCharacter(style.charAt(counter))) { | ||
| 697 | if (sb == null) { | ||
| 698 | sb = new StringBuilder(style); | ||
| 699 | } | ||
| 700 | sb.setCharAt(counter, 'a'); | ||
| 701 | } | ||
| 702 | } | ||
| 703 | String mappedName = (sb != null) ? sb.toString() : style; | ||
| 704 | while (styleNameMapping.get(mappedName) != null) { | ||
| 705 | mappedName = mappedName + 'x'; | ||
| 706 | } | ||
| 707 | styleNameMapping.put(style, mappedName); | ||
| 708 | return mappedName; | ||
| 709 | } | ||
| 710 | |||
| 711 | /** | ||
| 712 | * Returns the mapped style name corresponding to <code>style</code>. | ||
| 713 | */ | ||
| 714 | private String mapStyleName(String style) { | ||
| 715 | if (styleNameMapping == null) { | ||
| 716 | return style; | ||
| 717 | } | ||
| 718 | String retValue = styleNameMapping.get(style); | ||
| 719 | return (retValue == null) ? style : retValue; | ||
| 720 | } | ||
| 721 | |||
| 722 | private boolean isValidCharacter(char character) { | ||
| 723 | return ((character >= 'a' && character <= 'z') || | ||
| 724 | (character >= 'A' && character <= 'Z')); | ||
| 725 | } | ||
| 726 | } | ||