/* * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */package javax.swing.text.rtf; import java.lang.*;import java.util.*;import java.awt.Color;import java.awt.Font;import java.io.OutputStream;import java.io.IOException; import javax.swing.text.*; /** * Generates an RTF output stream (java.io.OutputStream) from rich text * (handed off through a series of LTTextAcceptor calls). Can be used to * generate RTF from any object which knows how to write to a text acceptor * (e.g., LTAttributedText and LTRTFFilter). * * <p>Note that this is a lossy conversion since RTF's model of * text does not exactly correspond with LightText's. * * @see LTAttributedText * @see LTRTFFilter * @see LTTextAcceptor * @see java.io.OutputStream */ class RTFGenerator extends Object{ /* These dictionaries map Colors, font names, or Style objects to Integers */ Dictionary<Object, Integer> colorTable; int colorCount; Dictionary<String, Integer> fontTable; int fontCount; Dictionary<AttributeSet, Integer> styleTable; int styleCount; /* where all the text is going */ OutputStream outputStream; boolean afterKeyword; MutableAttributeSet outputAttributes; /* the value of the last \\ucN keyword emitted */ int unicodeCount; /* for efficiency's sake (ha) */ private Segment workingSegment; int[] outputConversion; /** The default color, used for text without an explicit color * attribute. */ static public final Color defaultRTFColor = Color.black; static public final float defaultFontSize = 12f; static public final String defaultFontFamily = "Helvetica"; /* constants so we can avoid allocating objects in inner loops */ final static private Object MagicToken; /* An array of character-keyword pairs. This could be done as a dictionary (and lookup would be quicker), but that would require allocating an object for every character written (slow!). */ static class CharacterKeywordPair { public char character; public String keyword; } static protected CharacterKeywordPair[] textKeywords; static { MagicToken = new Object(); Dictionary textKeywordDictionary = RTFReader.textKeywords; Enumeration keys = textKeywordDictionary.keys(); Vector<CharacterKeywordPair> tempPairs = new Vector<CharacterKeywordPair>(); while(keys.hasMoreElements()) { CharacterKeywordPair pair = new CharacterKeywordPair(); pair.keyword = (String)keys.nextElement(); pair.character = ((String)textKeywordDictionary.get(pair.keyword)).charAt(0); tempPairs.addElement(pair); } textKeywords = new CharacterKeywordPair[tempPairs.size()]; tempPairs.copyInto(textKeywords); } static final char[] hexdigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; static public void writeDocument(Document d, OutputStream to) throws IOException{ RTFGenerator gen = new RTFGenerator(to); Element root = d.getDefaultRootElement(); gen.examineElement(root); gen.writeRTFHeader(); gen.writeDocumentProperties(d); /* TODO this assumes a particular element structure; is there a way to iterate more generically ? */ int max = root.getElementCount(); for(int idx = 0; idx < max; idx++) gen.writeParagraphElement(root.getElement(idx)); gen.writeRTFTrailer();} public RTFGenerator(OutputStream to){ colorTable = new Hashtable<Object, Integer>(); colorTable.put(defaultRTFColor, Integer.valueOf(0)); colorCount = 1; fontTable = new Hashtable<String, Integer>(); fontCount = 0; styleTable = new Hashtable<AttributeSet, Integer>(); /* TODO: put default style in style table */ styleCount = 0; workingSegment = new Segment(); outputStream = to; unicodeCount = 1;} public void examineElement(Element el){ AttributeSet a = el.getAttributes(); String fontName; Object foregroundColor, backgroundColor; tallyStyles(a); if (a != null) { /* TODO: default color must be color 0! */ foregroundColor = StyleConstants.getForeground(a); if (foregroundColor != null && colorTable.get(foregroundColor) == null) { colorTable.put(foregroundColor, new Integer(colorCount)); colorCount ++; } backgroundColor = a.getAttribute(StyleConstants.Background); if (backgroundColor != null && colorTable.get(backgroundColor) == null) { colorTable.put(backgroundColor, new Integer(colorCount)); colorCount ++; } fontName = StyleConstants.getFontFamily(a); if (fontName == null) fontName = defaultFontFamily; if (fontName != null && fontTable.get(fontName) == null) { fontTable.put(fontName, new Integer(fontCount)); fontCount ++; } } int el_count = el.getElementCount(); for(int el_idx = 0; el_idx < el_count; el_idx ++) { examineElement(el.getElement(el_idx)); }} private void tallyStyles(AttributeSet a) { while (a != null) { if (a instanceof Style) { Integer aNum = styleTable.get(a); if (aNum == null) { styleCount = styleCount + 1; aNum = new Integer(styleCount); styleTable.put(a, aNum); } } a = a.getResolveParent(); }} private Style findStyle(AttributeSet a){ while(a != null) { if (a instanceof Style) { Object aNum = styleTable.get(a); if (aNum != null) return (Style)a; } a = a.getResolveParent(); } return null;} private Integer findStyleNumber(AttributeSet a, String domain){ while(a != null) { if (a instanceof Style) { Integer aNum = styleTable.get(a); if (aNum != null) { if (domain == null || domain.equals(a.getAttribute(Constants.StyleType))) return aNum; } } a = a.getResolveParent(); } return null;} static private Object attrDiff(MutableAttributeSet oldAttrs, AttributeSet newAttrs, Object key, Object dfl){ Object oldValue, newValue; oldValue = oldAttrs.getAttribute(key); newValue = newAttrs.getAttribute(key); if (newValue == oldValue) return null; if (newValue == null) { oldAttrs.removeAttribute(key); if (dfl != null && !dfl.equals(oldValue)) return dfl; else return null; } if (oldValue == null || !equalArraysOK(oldValue, newValue)) { oldAttrs.addAttribute(key, newValue); return newValue; } return null;} static private boolean equalArraysOK(Object a, Object b){ Object[] aa, bb; if (a == b) return true; if (a == null || b == null) return false; if (a.equals(b)) return true; if (!(a.getClass().isArray() && b.getClass().isArray())) return false; aa = (Object[])a; bb = (Object[])b; if (aa.length != bb.length) return false; int i; int l = aa.length; for(i = 0; i < l; i++) { if (!equalArraysOK(aa[i], bb[i])) return false; } return true;} /* Writes a line break to the output file, for ease in debugging */public void writeLineBreak() throws IOException{ writeRawString("\n"); afterKeyword = false;} public void writeRTFHeader() throws IOException{ int index; /* TODO: Should the writer attempt to examine the text it's writing and pick a character set which will most compactly represent the document? (currently the writer always uses the ansi character set, which is roughly ISO-8859 Latin-1, and uses Unicode escapes for all other characters. However Unicode is a relatively recent addition to RTF, and not all readers will understand it.) */ writeBegingroup(); writeControlWord("rtf", 1); writeControlWord("ansi"); outputConversion = outputConversionForName("ansi"); writeLineBreak(); /* write font table */ String[] sortedFontTable = new String[fontCount]; Enumeration<String> fonts = fontTable.keys(); String font; while(fonts.hasMoreElements()) { font = fonts.nextElement(); Integer num = fontTable.get(font); sortedFontTable[num.intValue()] = font; } writeBegingroup(); writeControlWord("fonttbl"); for(index = 0; index < fontCount; index ++) { writeControlWord("f", index); writeControlWord("fnil"); /* TODO: supply correct font style */ writeText(sortedFontTable[index]); writeText(";"); } writeEndgroup(); writeLineBreak(); /* write color table */ if (colorCount > 1) { Color[] sortedColorTable = new Color[colorCount]; Enumeration colors = colorTable.keys(); Color color; while(colors.hasMoreElements()) { color = (Color)colors.nextElement(); Integer num = colorTable.get(color); sortedColorTable[num.intValue()] = color; } writeBegingroup(); writeControlWord("colortbl"); for(index = 0; index < colorCount; index ++) { color = sortedColorTable[index]; if (color != null) { writeControlWord("red", color.getRed()); writeControlWord("green", color.getGreen()); writeControlWord("blue", color.getBlue()); } writeRawString(";"); } writeEndgroup(); writeLineBreak(); } /* write the style sheet */ if (styleCount > 1) { writeBegingroup(); writeControlWord("stylesheet"); Enumeration<AttributeSet> styles = styleTable.keys(); while(styles.hasMoreElements()) { Style style = (Style)styles.nextElement(); int styleNumber = styleTable.get(style).intValue(); writeBegingroup(); String styleType = (String)style.getAttribute(Constants.StyleType); if (styleType == null) styleType = Constants.STParagraph; if (styleType.equals(Constants.STCharacter)) { writeControlWord("*"); writeControlWord("cs", styleNumber); } else if(styleType.equals(Constants.STSection)) { writeControlWord("*"); writeControlWord("ds", styleNumber); } else { writeControlWord("s", styleNumber); } AttributeSet basis = style.getResolveParent(); MutableAttributeSet goat; if (basis == null) { goat = new SimpleAttributeSet(); } else { goat = new SimpleAttributeSet(basis); } updateSectionAttributes(goat, style, false); updateParagraphAttributes(goat, style, false); updateCharacterAttributes(goat, style, false); basis = style.getResolveParent(); if (basis != null && basis instanceof Style) { Integer basedOn = styleTable.get(basis); if (basedOn != null) { writeControlWord("sbasedon", basedOn.intValue()); } } Style nextStyle = (Style)style.getAttribute(Constants.StyleNext); if (nextStyle != null) { Integer nextNum = styleTable.get(nextStyle); if (nextNum != null) { writeControlWord("snext", nextNum.intValue()); } } Boolean hidden = (Boolean)style.getAttribute(Constants.StyleHidden); if (hidden != null && hidden.booleanValue()) writeControlWord("shidden"); Boolean additive = (Boolean)style.getAttribute(Constants.StyleAdditive); if (additive != null && additive.booleanValue()) writeControlWord("additive"); writeText(style.getName()); writeText(";"); writeEndgroup(); } writeEndgroup(); writeLineBreak(); } outputAttributes = new SimpleAttributeSet();} void writeDocumentProperties(Document doc) throws IOException{ /* Write the document properties */ int i; boolean wroteSomething = false; for(i = 0; i < RTFAttributes.attributes.length; i++) { RTFAttribute attr = RTFAttributes.attributes[i]; if (attr.domain() != RTFAttribute.D_DOCUMENT) continue; Object prop = doc.getProperty(attr.swingName()); boolean ok = attr.writeValue(prop, this, false); if (ok) wroteSomething = true; } if (wroteSomething) writeLineBreak();} public void writeRTFTrailer() throws IOException{ writeEndgroup(); writeLineBreak();} protected void checkNumericControlWord(MutableAttributeSet currentAttributes, AttributeSet newAttributes, Object attrName, String controlWord, float dflt, float scale) throws IOException{ Object parm; if ((parm = attrDiff(currentAttributes, newAttributes, attrName, MagicToken)) != null) { float targ; if (parm == MagicToken) targ = dflt; else targ = ((Number)parm).floatValue(); writeControlWord(controlWord, Math.round(targ * scale)); }} protected void checkControlWord(MutableAttributeSet currentAttributes, AttributeSet newAttributes, RTFAttribute word) throws IOException{ Object parm; if ((parm = attrDiff(currentAttributes, newAttributes, word.swingName(), MagicToken)) != null) { if (parm == MagicToken) parm = null; word.writeValue(parm, this, true); }} protected void checkControlWords(MutableAttributeSet currentAttributes, AttributeSet newAttributes, RTFAttribute words[],Lines 1–500 of 1001. Load more
/* * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package javax.swing.text.rtf; import java.lang.*; import java.util.*; import java.awt.Color; import java.awt.Font; import java.io.OutputStream; import java.io.IOException; import javax.swing.text.*; /** * Generates an RTF output stream (java.io.OutputStream) from rich text * (handed off through a series of LTTextAcceptor calls). Can be used to * generate RTF from any object which knows how to write to a text acceptor * (e.g., LTAttributedText and LTRTFFilter). * * <p>Note that this is a lossy conversion since RTF's model of * text does not exactly correspond with LightText's. * * @see LTAttributedText * @see LTRTFFilter * @see LTTextAcceptor * @see java.io.OutputStream */ class RTFGenerator extends Object { /* These dictionaries map Colors, font names, or Style objects to Integers */ Dictionary<Object, Integer> colorTable; int colorCount; Dictionary<String, Integer> fontTable; int fontCount; Dictionary<AttributeSet, Integer> styleTable; int styleCount; /* where all the text is going */ OutputStream outputStream; boolean afterKeyword; MutableAttributeSet outputAttributes; /* the value of the last \\ucN keyword emitted */ int unicodeCount; /* for efficiency's sake (ha) */ private Segment workingSegment; int[] outputConversion; /** The default color, used for text without an explicit color * attribute. */ static public final Color defaultRTFColor = Color.black; static public final float defaultFontSize = 12f; static public final String defaultFontFamily = "Helvetica"; /* constants so we can avoid allocating objects in inner loops */ final static private Object MagicToken; /* An array of character-keyword pairs. This could be done as a dictionary (and lookup would be quicker), but that would require allocating an object for every character written (slow!). */ static class CharacterKeywordPair { public char character; public String keyword; } static protected CharacterKeywordPair[] textKeywords; static { MagicToken = new Object(); Dictionary textKeywordDictionary = RTFReader.textKeywords; Enumeration keys = textKeywordDictionary.keys(); Vector<CharacterKeywordPair> tempPairs = new Vector<CharacterKeywordPair>(); while(keys.hasMoreElements()) { CharacterKeywordPair pair = new CharacterKeywordPair(); pair.keyword = (String)keys.nextElement(); pair.character = ((String)textKeywordDictionary.get(pair.keyword)).charAt(0); tempPairs.addElement(pair); } textKeywords = new CharacterKeywordPair[tempPairs.size()]; tempPairs.copyInto(textKeywords); } static final char[] hexdigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; static public void writeDocument(Document d, OutputStream to) throws IOException { RTFGenerator gen = new RTFGenerator(to); Element root = d.getDefaultRootElement(); gen.examineElement(root); gen.writeRTFHeader(); gen.writeDocumentProperties(d); /* TODO this assumes a particular element structure; is there a way to iterate more generically ? */ int max = root.getElementCount(); for(int idx = 0; idx < max; idx++) gen.writeParagraphElement(root.getElement(idx)); gen.writeRTFTrailer(); } public RTFGenerator(OutputStream to) { colorTable = new Hashtable<Object, Integer>(); colorTable.put(defaultRTFColor, Integer.valueOf(0)); colorCount = 1; fontTable = new Hashtable<String, Integer>(); fontCount = 0; styleTable = new Hashtable<AttributeSet, Integer>(); /* TODO: put default style in style table */ styleCount = 0; workingSegment = new Segment(); outputStream = to; unicodeCount = 1; } public void examineElement(Element el) { AttributeSet a = el.getAttributes(); String fontName; Object foregroundColor, backgroundColor; tallyStyles(a); if (a != null) { /* TODO: default color must be color 0! */ foregroundColor = StyleConstants.getForeground(a); if (foregroundColor != null && colorTable.get(foregroundColor) == null) { colorTable.put(foregroundColor, new Integer(colorCount)); colorCount ++; } backgroundColor = a.getAttribute(StyleConstants.Background); if (backgroundColor != null && colorTable.get(backgroundColor) == null) { colorTable.put(backgroundColor, new Integer(colorCount)); colorCount ++; } fontName = StyleConstants.getFontFamily(a); if (fontName == null) fontName = defaultFontFamily; if (fontName != null && fontTable.get(fontName) == null) { fontTable.put(fontName, new Integer(fontCount)); fontCount ++; } } int el_count = el.getElementCount(); for(int el_idx = 0; el_idx < el_count; el_idx ++) { examineElement(el.getElement(el_idx)); } } private void tallyStyles(AttributeSet a) { while (a != null) { if (a instanceof Style) { Integer aNum = styleTable.get(a); if (aNum == null) { styleCount = styleCount + 1; aNum = new Integer(styleCount); styleTable.put(a, aNum); } } a = a.getResolveParent(); } } private Style findStyle(AttributeSet a) { while(a != null) { if (a instanceof Style) { Object aNum = styleTable.get(a); if (aNum != null) return (Style)a; } a = a.getResolveParent(); } return null; } private Integer findStyleNumber(AttributeSet a, String domain) { while(a != null) { if (a instanceof Style) { Integer aNum = styleTable.get(a); if (aNum != null) { if (domain == null || domain.equals(a.getAttribute(Constants.StyleType))) return aNum; } } a = a.getResolveParent(); } return null; } static private Object attrDiff(MutableAttributeSet oldAttrs, AttributeSet newAttrs, Object key, Object dfl) { Object oldValue, newValue; oldValue = oldAttrs.getAttribute(key); newValue = newAttrs.getAttribute(key); if (newValue == oldValue) return null; if (newValue == null) { oldAttrs.removeAttribute(key); if (dfl != null && !dfl.equals(oldValue)) return dfl; else return null; } if (oldValue == null || !equalArraysOK(oldValue, newValue)) { oldAttrs.addAttribute(key, newValue); return newValue; } return null; } static private boolean equalArraysOK(Object a, Object b) { Object[] aa, bb; if (a == b) return true; if (a == null || b == null) return false; if (a.equals(b)) return true; if (!(a.getClass().isArray() && b.getClass().isArray())) return false; aa = (Object[])a; bb = (Object[])b; if (aa.length != bb.length) return false; int i; int l = aa.length; for(i = 0; i < l; i++) { if (!equalArraysOK(aa[i], bb[i])) return false; } return true; } /* Writes a line break to the output file, for ease in debugging */ public void writeLineBreak() throws IOException { writeRawString("\n"); afterKeyword = false; } public void writeRTFHeader() throws IOException { int index; /* TODO: Should the writer attempt to examine the text it's writing and pick a character set which will most compactly represent the document? (currently the writer always uses the ansi character set, which is roughly ISO-8859 Latin-1, and uses Unicode escapes for all other characters. However Unicode is a relatively recent addition to RTF, and not all readers will understand it.) */ writeBegingroup(); writeControlWord("rtf", 1); writeControlWord("ansi"); outputConversion = outputConversionForName("ansi"); writeLineBreak(); /* write font table */ String[] sortedFontTable = new String[fontCount]; Enumeration<String> fonts = fontTable.keys(); String font; while(fonts.hasMoreElements()) { font = fonts.nextElement(); Integer num = fontTable.get(font); sortedFontTable[num.intValue()] = font; } writeBegingroup(); writeControlWord("fonttbl"); for(index = 0; index < fontCount; index ++) { writeControlWord("f", index); writeControlWord("fnil"); /* TODO: supply correct font style */ writeText(sortedFontTable[index]); writeText(";"); } writeEndgroup(); writeLineBreak(); /* write color table */ if (colorCount > 1) { Color[] sortedColorTable = new Color[colorCount]; Enumeration colors = colorTable.keys(); Color color; while(colors.hasMoreElements()) { color = (Color)colors.nextElement(); Integer num = colorTable.get(color); sortedColorTable[num.intValue()] = color; } writeBegingroup(); writeControlWord("colortbl"); for(index = 0; index < colorCount; index ++) { color = sortedColorTable[index]; if (color != null) { writeControlWord("red", color.getRed()); writeControlWord("green", color.getGreen()); writeControlWord("blue", color.getBlue()); } writeRawString(";"); } writeEndgroup(); writeLineBreak(); } /* write the style sheet */ if (styleCount > 1) { writeBegingroup(); writeControlWord("stylesheet"); Enumeration<AttributeSet> styles = styleTable.keys(); while(styles.hasMoreElements()) { Style style = (Style)styles.nextElement(); int styleNumber = styleTable.get(style).intValue(); writeBegingroup(); String styleType = (String)style.getAttribute(Constants.StyleType); if (styleType == null) styleType = Constants.STParagraph; if (styleType.equals(Constants.STCharacter)) { writeControlWord("*"); writeControlWord("cs", styleNumber); } else if(styleType.equals(Constants.STSection)) { writeControlWord("*"); writeControlWord("ds", styleNumber); } else { writeControlWord("s", styleNumber); } AttributeSet basis = style.getResolveParent(); MutableAttributeSet goat; if (basis == null) { goat = new SimpleAttributeSet(); } else { goat = new SimpleAttributeSet(basis); } updateSectionAttributes(goat, style, false); updateParagraphAttributes(goat, style, false); updateCharacterAttributes(goat, style, false); basis = style.getResolveParent(); if (basis != null && basis instanceof Style) { Integer basedOn = styleTable.get(basis); if (basedOn != null) { writeControlWord("sbasedon", basedOn.intValue()); } } Style nextStyle = (Style)style.getAttribute(Constants.StyleNext); if (nextStyle != null) { Integer nextNum = styleTable.get(nextStyle); if (nextNum != null) { writeControlWord("snext", nextNum.intValue()); } } Boolean hidden = (Boolean)style.getAttribute(Constants.StyleHidden); if (hidden != null && hidden.booleanValue()) writeControlWord("shidden"); Boolean additive = (Boolean)style.getAttribute(Constants.StyleAdditive); if (additive != null && additive.booleanValue()) writeControlWord("additive"); writeText(style.getName()); writeText(";"); writeEndgroup(); } writeEndgroup(); writeLineBreak(); } outputAttributes = new SimpleAttributeSet(); } void writeDocumentProperties(Document doc) throws IOException { /* Write the document properties */ int i; boolean wroteSomething = false; for(i = 0; i < RTFAttributes.attributes.length; i++) { RTFAttribute attr = RTFAttributes.attributes[i]; if (attr.domain() != RTFAttribute.D_DOCUMENT) continue; Object prop = doc.getProperty(attr.swingName()); boolean ok = attr.writeValue(prop, this, false); if (ok) wroteSomething = true; } if (wroteSomething) writeLineBreak(); } public void writeRTFTrailer() throws IOException { writeEndgroup(); writeLineBreak(); } protected void checkNumericControlWord(MutableAttributeSet currentAttributes, AttributeSet newAttributes, Object attrName, String controlWord, float dflt, float scale) throws IOException { Object parm; if ((parm = attrDiff(currentAttributes, newAttributes, attrName, MagicToken)) != null) { float targ; if (parm == MagicToken) targ = dflt; else targ = ((Number)parm).floatValue(); writeControlWord(controlWord, Math.round(targ * scale)); } } protected void checkControlWord(MutableAttributeSet currentAttributes, AttributeSet newAttributes, RTFAttribute word) throws IOException { Object parm; if ((parm = attrDiff(currentAttributes, newAttributes, word.swingName(), MagicToken)) != null) { if (parm == MagicToken) parm = null; word.writeValue(parm, this, true); } } protected void checkControlWords(MutableAttributeSet currentAttributes, AttributeSet newAttributes, RTFAttribute words[],