| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
package javax.swing.text.rtf;  | 
 | 
 | 
 | 
import java.lang.*;  | 
 | 
import java.util.*;  | 
 | 
import java.io.*;  | 
 | 
import java.awt.Color;  | 
 | 
import java.security.AccessController;  | 
 | 
import java.security.PrivilegedAction;  | 
 | 
import javax.swing.text.*;  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
class RTFReader extends RTFParser  | 
 | 
{ | 
 | 
    | 
 | 
  StyledDocument target;  | 
 | 
 | 
 | 
    | 
 | 
 | 
 | 
   *  or ends. */  | 
 | 
  Dictionary<Object, Object> parserState;   /* Current parser state */  | 
 | 
    | 
 | 
 | 
 | 
   *  variable for speed. */  | 
 | 
  Destination rtfDestination;  | 
 | 
    | 
 | 
  MutableAttributeSet documentAttributes;  | 
 | 
 | 
 | 
    | 
 | 
  Dictionary<Integer, String> fontTable;  | 
 | 
    | 
 | 
  Color[] colorTable;  | 
 | 
    | 
 | 
  Style[] characterStyles;  | 
 | 
    | 
 | 
  Style[] paragraphStyles;  | 
 | 
    | 
 | 
  Style[] sectionStyles;  | 
 | 
 | 
 | 
    | 
 | 
   *  The version information is currently not used. */  | 
 | 
  int rtfversion;  | 
 | 
 | 
 | 
    | 
 | 
   *  the containing group should be ignored. */  | 
 | 
  boolean ignoreGroupIfUnknownKeyword;  | 
 | 
 | 
 | 
    | 
 | 
 | 
 | 
   *  Unicode character. */  | 
 | 
  int skippingCharacters;  | 
 | 
 | 
 | 
  static private Dictionary<String, RTFAttribute> straightforwardAttributes;  | 
 | 
  static { | 
 | 
      straightforwardAttributes = RTFAttributes.attributesByKeyword();  | 
 | 
  }  | 
 | 
 | 
 | 
  private MockAttributeSet mockery;  | 
 | 
 | 
 | 
  /* this should be final, but there's a bug in javac... */  | 
 | 
    | 
 | 
   *  for those keywords which simply insert some text. */  | 
 | 
  static Dictionary<String, String> textKeywords = null;  | 
 | 
  static { | 
 | 
      textKeywords = new Hashtable<String, String>();  | 
 | 
      textKeywords.put("\\",         "\\"); | 
 | 
      textKeywords.put("{",          "{"); | 
 | 
      textKeywords.put("}",          "}"); | 
 | 
      textKeywords.put(" ",          "\u00A0");   | 
 | 
      textKeywords.put("~",          "\u00A0");   | 
 | 
      textKeywords.put("_",          "\u2011");   | 
 | 
      textKeywords.put("bullet",     "\u2022"); | 
 | 
      textKeywords.put("emdash",     "\u2014"); | 
 | 
      textKeywords.put("emspace",    "\u2003"); | 
 | 
      textKeywords.put("endash",     "\u2013"); | 
 | 
      textKeywords.put("enspace",    "\u2002"); | 
 | 
      textKeywords.put("ldblquote",  "\u201C"); | 
 | 
      textKeywords.put("lquote",     "\u2018"); | 
 | 
      textKeywords.put("ltrmark",    "\u200E"); | 
 | 
      textKeywords.put("rdblquote",  "\u201D"); | 
 | 
      textKeywords.put("rquote",     "\u2019"); | 
 | 
      textKeywords.put("rtlmark",    "\u200F"); | 
 | 
      textKeywords.put("tab",        "\u0009"); | 
 | 
      textKeywords.put("zwj",        "\u200D"); | 
 | 
      textKeywords.put("zwnj",       "\u200C"); | 
 | 
 | 
 | 
        | 
 | 
         I can tell. */  | 
 | 
      textKeywords.put("-",          "\u2027");  /* TODO: optional hyphen */ | 
 | 
  }  | 
 | 
 | 
 | 
    | 
 | 
  static final String TabAlignmentKey = "tab_alignment";  | 
 | 
  static final String TabLeaderKey = "tab_leader";  | 
 | 
 | 
 | 
  static Dictionary<String, char[]> characterSets;  | 
 | 
  static boolean useNeXTForAnsi = false;  | 
 | 
  static { | 
 | 
      characterSets = new Hashtable<String, char[]>();  | 
 | 
  }  | 
 | 
 | 
 | 
/* TODO: per-font font encodings ( \fcharset control word ) ? */  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
public RTFReader(StyledDocument destination)  | 
 | 
{ | 
 | 
    int i;  | 
 | 
 | 
 | 
    target = destination;  | 
 | 
    parserState = new Hashtable<Object, Object>();  | 
 | 
    fontTable = new Hashtable<Integer, String>();  | 
 | 
 | 
 | 
    rtfversion = -1;  | 
 | 
 | 
 | 
    mockery = new MockAttributeSet();  | 
 | 
    documentAttributes = new SimpleAttributeSet();  | 
 | 
}  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
public void handleBinaryBlob(byte[] data)  | 
 | 
{ | 
 | 
    if (skippingCharacters > 0) { | 
 | 
          | 
 | 
        skippingCharacters --;  | 
 | 
        return;  | 
 | 
    }  | 
 | 
 | 
 | 
    /* someday, someone will want to do something with blobs */  | 
 | 
}  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 * stream. Called by the superclass. */  | 
 | 
public void handleText(String text)  | 
 | 
{ | 
 | 
    if (skippingCharacters > 0) { | 
 | 
        if (skippingCharacters >= text.length()) { | 
 | 
            skippingCharacters -= text.length();  | 
 | 
            return;  | 
 | 
        } else { | 
 | 
            text = text.substring(skippingCharacters);  | 
 | 
            skippingCharacters = 0;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    if (rtfDestination != null) { | 
 | 
        rtfDestination.handleText(text);  | 
 | 
        return;  | 
 | 
    }  | 
 | 
 | 
 | 
    warning("Text with no destination. oops."); | 
 | 
}  | 
 | 
 | 
 | 
 | 
 | 
Color defaultColor()  | 
 | 
{ | 
 | 
    return Color.black;  | 
 | 
}  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
public void begingroup()  | 
 | 
{ | 
 | 
    if (skippingCharacters > 0) { | 
 | 
          | 
 | 
        skippingCharacters = 0;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
       immediately throwing it away. */  | 
 | 
    Object oldSaveState = parserState.get("_savedState"); | 
 | 
    if (oldSaveState != null)  | 
 | 
        parserState.remove("_savedState"); | 
 | 
    Dictionary<String, Object> saveState = (Dictionary<String, Object>)((Hashtable)parserState).clone();  | 
 | 
    if (oldSaveState != null)  | 
 | 
        saveState.put("_savedState", oldSaveState); | 
 | 
    parserState.put("_savedState", saveState); | 
 | 
 | 
 | 
    if (rtfDestination != null)  | 
 | 
        rtfDestination.begingroup();  | 
 | 
}  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
public void endgroup()  | 
 | 
{ | 
 | 
    if (skippingCharacters > 0) { | 
 | 
          | 
 | 
        skippingCharacters = 0;  | 
 | 
    }  | 
 | 
 | 
 | 
    Dictionary<Object, Object> restoredState = (Dictionary<Object, Object>)parserState.get("_savedState"); | 
 | 
    Destination restoredDestination = (Destination)restoredState.get("dst"); | 
 | 
    if (restoredDestination != rtfDestination) { | 
 | 
        rtfDestination.close();   | 
 | 
        rtfDestination = restoredDestination;  | 
 | 
    }  | 
 | 
    Dictionary oldParserState = parserState;  | 
 | 
    parserState = restoredState;  | 
 | 
    if (rtfDestination != null)  | 
 | 
        rtfDestination.endgroup(oldParserState);  | 
 | 
}  | 
 | 
 | 
 | 
protected void setRTFDestination(Destination newDestination)  | 
 | 
{ | 
 | 
      | 
 | 
       current destination (should never happen) */  | 
 | 
    Dictionary previousState = (Dictionary)parserState.get("_savedState"); | 
 | 
    if (previousState != null) { | 
 | 
        if (rtfDestination != previousState.get("dst")) { | 
 | 
            warning("Warning, RTF destination overridden, invalid RTF."); | 
 | 
            rtfDestination.close();  | 
 | 
        }  | 
 | 
    }  | 
 | 
    rtfDestination = newDestination;  | 
 | 
    parserState.put("dst", rtfDestination); | 
 | 
}  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
public void close()  | 
 | 
    throws IOException  | 
 | 
{ | 
 | 
    Enumeration docProps = documentAttributes.getAttributeNames();  | 
 | 
    while(docProps.hasMoreElements()) { | 
 | 
        Object propName = docProps.nextElement();  | 
 | 
        target.putProperty(propName,  | 
 | 
                           documentAttributes.getAttribute(propName));  | 
 | 
    }  | 
 | 
 | 
 | 
    /* RTFParser should have ensured that all our groups are closed */  | 
 | 
 | 
 | 
    warning("RTF filter done."); | 
 | 
 | 
 | 
    super.close();  | 
 | 
}  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
public boolean handleKeyword(String keyword)  | 
 | 
{ | 
 | 
    String item;  | 
 | 
    boolean ignoreGroupIfUnknownKeywordSave = ignoreGroupIfUnknownKeyword;  | 
 | 
 | 
 | 
    if (skippingCharacters > 0) { | 
 | 
        skippingCharacters --;  | 
 | 
        return true;  | 
 | 
    }  | 
 | 
 | 
 | 
    ignoreGroupIfUnknownKeyword = false;  | 
 | 
 | 
 | 
    if ((item = textKeywords.get(keyword)) != null) { | 
 | 
        handleText(item);  | 
 | 
        return true;  | 
 | 
    }  | 
 | 
 | 
 | 
    if (keyword.equals("fonttbl")) { | 
 | 
        setRTFDestination(new FonttblDestination());  | 
 | 
        return true;  | 
 | 
    }  | 
 | 
 | 
 | 
    if (keyword.equals("colortbl")) { | 
 | 
        setRTFDestination(new ColortblDestination());  | 
 | 
        return true;  | 
 | 
    }  | 
 | 
 | 
 | 
    if (keyword.equals("stylesheet")) { | 
 | 
        setRTFDestination(new StylesheetDestination());  | 
 | 
        return true;  | 
 | 
    }  | 
 | 
 | 
 | 
    if (keyword.equals("info")) { | 
 | 
        setRTFDestination(new InfoDestination());  | 
 | 
        return false;  | 
 | 
    }  | 
 | 
 | 
 | 
    if (keyword.equals("mac")) { | 
 | 
        setCharacterSet("mac"); | 
 | 
        return true;  | 
 | 
    }  | 
 | 
 | 
 | 
    if (keyword.equals("ansi")) { | 
 | 
        if (useNeXTForAnsi)  | 
 | 
            setCharacterSet("NeXT"); | 
 | 
        else  | 
 | 
            setCharacterSet("ansi"); | 
 | 
        return true;  | 
 | 
    }  | 
 | 
 | 
 | 
    if (keyword.equals("next")) { | 
 | 
        setCharacterSet("NeXT"); | 
 | 
        return true;  | 
 | 
    }  | 
 | 
 | 
 | 
    if (keyword.equals("pc")) { | 
 | 
        setCharacterSet("cpg437");  | 
 | 
        return true;  | 
 | 
    }  | 
 | 
 | 
 | 
    if (keyword.equals("pca")) { | 
 | 
        setCharacterSet("cpg850");  | 
 | 
        return true;  | 
 | 
    }  | 
 | 
 | 
 | 
    if (keyword.equals("*")) { | 
 | 
        ignoreGroupIfUnknownKeyword = true;  | 
 | 
        return true;  | 
 | 
    }  | 
 | 
 | 
 | 
    if (rtfDestination != null) { | 
 | 
        if(rtfDestination.handleKeyword(keyword))  | 
 | 
            return true;  | 
 | 
    }  | 
 | 
 | 
 | 
    /* this point is reached only if the keyword is unrecognized */  | 
 | 
 | 
 | 
      | 
 | 
    if (keyword.equals("aftncn") || | 
 | 
        keyword.equals("aftnsep") || | 
 | 
        keyword.equals("aftnsepc") || | 
 | 
        keyword.equals("annotation") || | 
 | 
        keyword.equals("atnauthor") || | 
 | 
        keyword.equals("atnicn") || | 
 | 
        keyword.equals("atnid") || | 
 | 
        keyword.equals("atnref") || | 
 | 
        keyword.equals("atntime") || | 
 | 
        keyword.equals("atrfend") || | 
 | 
        keyword.equals("atrfstart") || | 
 | 
        keyword.equals("bkmkend") || | 
 | 
        keyword.equals("bkmkstart") || | 
 | 
        keyword.equals("datafield") || | 
 | 
        keyword.equals("do") || | 
 | 
        keyword.equals("dptxbxtext") || | 
 | 
        keyword.equals("falt") || | 
 | 
        keyword.equals("field") || | 
 | 
        keyword.equals("file") || | 
 | 
        keyword.equals("filetbl") || | 
 | 
        keyword.equals("fname") || | 
 | 
        keyword.equals("fontemb") || | 
 | 
        keyword.equals("fontfile") || | 
 | 
        keyword.equals("footer") || | 
 | 
        keyword.equals("footerf") || | 
 | 
        keyword.equals("footerl") || | 
 | 
        keyword.equals("footerr") || | 
 | 
        keyword.equals("footnote") || | 
 | 
        keyword.equals("ftncn") || | 
 | 
        keyword.equals("ftnsep") || | 
 | 
        keyword.equals("ftnsepc") || | 
 | 
        keyword.equals("header") || | 
 | 
        keyword.equals("headerf") || | 
 | 
        keyword.equals("headerl") || | 
 | 
        keyword.equals("headerr") || | 
 | 
        keyword.equals("keycode") || | 
 | 
        keyword.equals("nextfile") || | 
 | 
        keyword.equals("object") || | 
 | 
        keyword.equals("pict") || | 
 | 
        keyword.equals("pn") || | 
 | 
        keyword.equals("pnseclvl") || | 
 | 
        keyword.equals("pntxtb") || | 
 | 
        keyword.equals("pntxta") || | 
 | 
        keyword.equals("revtbl") || | 
 | 
        keyword.equals("rxe") || | 
 | 
        keyword.equals("tc") || | 
 | 
        keyword.equals("template") || | 
 | 
        keyword.equals("txe") || | 
 | 
        keyword.equals("xe")) { | 
 | 
        ignoreGroupIfUnknownKeywordSave = true;  | 
 | 
    }  | 
 | 
 | 
 | 
    if (ignoreGroupIfUnknownKeywordSave) { | 
 | 
        setRTFDestination(new DiscardingDestination());  | 
 | 
    }  | 
 | 
 | 
 | 
    return false;  | 
 | 
}  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
public boolean handleKeyword(String keyword, int parameter)  | 
 | 
{ | 
 | 
    boolean ignoreGroupIfUnknownKeywordSave = ignoreGroupIfUnknownKeyword;  | 
 | 
 | 
 | 
    if (skippingCharacters > 0) { | 
 | 
        skippingCharacters --;  | 
 | 
        return true;  | 
 | 
    }  | 
 | 
 | 
 | 
    ignoreGroupIfUnknownKeyword = false;  | 
 | 
 | 
 | 
    if (keyword.equals("uc")) { | 
 | 
          | 
 | 
        parserState.put("UnicodeSkip", Integer.valueOf(parameter)); | 
 | 
        return true;  | 
 | 
    }  | 
 | 
    if (keyword.equals("u")) { | 
 | 
        if (parameter < 0)  | 
 | 
            parameter = parameter + 65536;  | 
 | 
        handleText((char)parameter);  | 
 | 
        Number skip = (Number)(parserState.get("UnicodeSkip")); | 
 | 
        if (skip != null) { | 
 | 
            skippingCharacters = skip.intValue();  | 
 | 
        } else { | 
 | 
            skippingCharacters = 1;  | 
 | 
        }  | 
 | 
        return true;  | 
 | 
    }  | 
 | 
 | 
 | 
    if (keyword.equals("rtf")) { | 
 | 
        rtfversion = parameter;  | 
 | 
        setRTFDestination(new DocumentDestination());  | 
 | 
        return true;  | 
 | 
    }  | 
 | 
 | 
 | 
    if (keyword.startsWith("NeXT") || | 
 | 
        keyword.equals("private")) | 
 | 
        ignoreGroupIfUnknownKeywordSave = true;  | 
 | 
 | 
 | 
    if (rtfDestination != null) { | 
 | 
        if(rtfDestination.handleKeyword(keyword, parameter))  | 
 | 
            return true;  | 
 | 
    }  | 
 | 
 | 
 | 
    /* this point is reached only if the keyword is unrecognized */  | 
 | 
 | 
 | 
    if (ignoreGroupIfUnknownKeywordSave) { | 
 | 
        setRTFDestination(new DiscardingDestination());  | 
 | 
    }  | 
 | 
 | 
 | 
    return false;  | 
 | 
}  | 
 | 
 | 
 | 
private void setTargetAttribute(String name, Object value)  | 
 | 
{ | 
 | 
//    target.changeAttributes(new LFDictionary(LFArray.arrayWithObject(value), LFArray.arrayWithObject(name)));  | 
 | 
}  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
public void setCharacterSet(String name)  | 
 | 
{ | 
 | 
    Object set;  | 
 | 
 | 
 | 
    try { | 
 | 
        set = getCharacterSet(name);  | 
 | 
    } catch (Exception e) { | 
 | 
        warning("Exception loading RTF character set \"" + name + "\": " + e); | 
 | 
        set = null;  | 
 | 
    }  | 
 | 
 | 
 | 
    if (set != null) { | 
 | 
        translationTable = (char[])set;  | 
 | 
    } else { | 
 | 
        warning("Unknown RTF character set \"" + name + "\""); | 
 | 
        if (!name.equals("ansi")) { | 
 | 
            try { | 
 | 
                translationTable = (char[])getCharacterSet("ansi"); | 
 | 
            } catch (IOException e) { | 
 | 
                throw new InternalError("RTFReader: Unable to find character set resources (" + e + ")", e); | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    setTargetAttribute(Constants.RTFCharacterSet, name);  | 
 | 
}  | 
 | 
 | 
 | 
 | 
 | 
 *  of known character sets */  | 
 | 
public static void  | 
 | 
defineCharacterSet(String name, char[] table)  | 
 | 
{ | 
 | 
    if (table.length < 256)  | 
 | 
        throw new IllegalArgumentException("Translation table must have 256 entries."); | 
 | 
    characterSets.put(name, table);  | 
 | 
}  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
public static Object  | 
 | 
getCharacterSet(final String name)  | 
 | 
    throws IOException  | 
 | 
{ | 
 | 
    char[] set = characterSets.get(name);  | 
 | 
    if (set == null) { | 
 | 
        InputStream charsetStream = AccessController.doPrivileged(  | 
 | 
                new PrivilegedAction<InputStream>() { | 
 | 
                    public InputStream run() { | 
 | 
                        return RTFReader.class.getResourceAsStream("charsets/" + name + ".txt"); | 
 | 
                    }  | 
 | 
                });  | 
 | 
        set = readCharset(charsetStream);  | 
 | 
        defineCharacterSet(name, set);  | 
 | 
    }  | 
 | 
    return set;  | 
 | 
}  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
static char[] readCharset(InputStream strm)  | 
 | 
     throws IOException  | 
 | 
{ | 
 | 
    char[] values = new char[256];  | 
 | 
    int i;  | 
 | 
    StreamTokenizer in = new StreamTokenizer(new BufferedReader(  | 
 | 
            new InputStreamReader(strm, "ISO-8859-1")));  | 
 | 
 | 
 | 
    in.eolIsSignificant(false);  | 
 | 
    in.commentChar('#'); | 
 | 
    in.slashSlashComments(true);  | 
 | 
    in.slashStarComments(true);  | 
 | 
 | 
 | 
    i = 0;  | 
 | 
    while (i < 256) { | 
 | 
        int ttype;  | 
 | 
        try { | 
 | 
            ttype = in.nextToken();  | 
 | 
        } catch (Exception e) { | 
 | 
            throw new IOException("Unable to read from character set file (" + e + ")"); | 
 | 
        }  | 
 | 
        if (ttype != in.TT_NUMBER) { | 
 | 
 | 
 | 
            throw new IOException("Unexpected token in character set file"); | 
 | 
//          continue;  | 
 | 
        }  | 
 | 
        values[i] = (char)(in.nval);  | 
 | 
        i++;  | 
 | 
    }  | 
 | 
 | 
 | 
    return values;  | 
 | 
}  | 
 | 
 | 
 | 
static char[] readCharset(java.net.URL href)  | 
 | 
     throws IOException  | 
 | 
{ | 
 | 
    return readCharset(href.openStream());  | 
 | 
}  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
interface Destination { | 
 | 
    void handleBinaryBlob(byte[] data);  | 
 | 
    void handleText(String text);  | 
 | 
    boolean handleKeyword(String keyword);  | 
 | 
    boolean handleKeyword(String keyword, int parameter);  | 
 | 
 | 
 | 
    void begingroup();  | 
 | 
    void endgroup(Dictionary oldState);  | 
 | 
 | 
 | 
    void close();  | 
 | 
}  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 *  It accepts all keywords and text but does nothing with them. */  | 
 | 
class DiscardingDestination implements Destination  | 
 | 
{ | 
 | 
    public void handleBinaryBlob(byte[] data)  | 
 | 
    { | 
 | 
        /* Discard binary blobs. */  | 
 | 
    }  | 
 | 
 | 
 | 
    public void handleText(String text)  | 
 | 
    { | 
 | 
        /* Discard text. */  | 
 | 
    }  | 
 | 
 | 
 | 
    public boolean handleKeyword(String text)  | 
 | 
    { | 
 | 
          | 
 | 
        return true;  | 
 | 
    }  | 
 | 
 | 
 | 
    public boolean handleKeyword(String text, int parameter)  | 
 | 
    { | 
 | 
          | 
 | 
        return true;  | 
 | 
    }  | 
 | 
 | 
 | 
    public void begingroup()  | 
 | 
    { | 
 | 
        /* Ignore groups --- the RTFReader will keep track of the  | 
 | 
           current group level as necessary */  | 
 | 
    }  | 
 | 
 | 
 | 
    public void endgroup(Dictionary oldState)  | 
 | 
    { | 
 | 
        /* Ignore groups */  | 
 | 
    }  | 
 | 
 | 
 | 
    public void close()  | 
 | 
    { | 
 | 
        /* No end-of-destination cleanup needed */  | 
 | 
    }  | 
 | 
}  | 
 | 
 | 
 | 
 | 
 | 
 *  fontTable dictionary. */  | 
 | 
class FonttblDestination implements Destination  | 
 | 
{ | 
 | 
    int nextFontNumber;  | 
 | 
    Integer fontNumberKey = null;  | 
 | 
    String nextFontFamily;  | 
 | 
 | 
 | 
    public void handleBinaryBlob(byte[] data)  | 
 | 
    { /* Discard binary blobs. */ } | 
 | 
 | 
 | 
    public void handleText(String text)  | 
 | 
    { | 
 | 
        int semicolon = text.indexOf(';'); | 
 | 
        String fontName;  | 
 | 
 | 
 | 
        if (semicolon > -1)  | 
 | 
            fontName = text.substring(0, semicolon);  | 
 | 
        else  | 
 | 
            fontName = text;  | 
 | 
 | 
 | 
 | 
 | 
        /* TODO: do something with the font family. */  | 
 | 
 | 
 | 
        if (nextFontNumber == -1  | 
 | 
            && fontNumberKey != null) { | 
 | 
              | 
 | 
            fontName = fontTable.get(fontNumberKey) + fontName;  | 
 | 
        } else { | 
 | 
            fontNumberKey = Integer.valueOf(nextFontNumber);  | 
 | 
        }  | 
 | 
        fontTable.put(fontNumberKey, fontName);  | 
 | 
 | 
 | 
        nextFontNumber = -1;  | 
 | 
        nextFontFamily = null;  | 
 | 
    }  | 
 | 
 | 
 | 
    public boolean handleKeyword(String keyword)  | 
 | 
    { | 
 | 
        if (keyword.charAt(0) == 'f') { | 
 | 
            nextFontFamily = keyword.substring(1);  | 
 | 
            return true;  | 
 | 
        }  | 
 | 
 | 
 | 
        return false;  | 
 | 
    }  | 
 | 
 | 
 | 
    public boolean handleKeyword(String keyword, int parameter)  | 
 | 
    { | 
 | 
        if (keyword.equals("f")) { | 
 | 
            nextFontNumber = parameter;  | 
 | 
            return true;  | 
 | 
        }  | 
 | 
 | 
 | 
        return false;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
    public void begingroup() {} | 
 | 
    public void endgroup(Dictionary oldState) {} | 
 | 
 | 
 | 
      | 
 | 
       dump its contents to the debugging log. */  | 
 | 
    public void close()  | 
 | 
    { | 
 | 
        Enumeration<Integer> nums = fontTable.keys();  | 
 | 
        warning("Done reading font table."); | 
 | 
        while(nums.hasMoreElements()) { | 
 | 
            Integer num = nums.nextElement();  | 
 | 
            warning("Number " + num + ": " + fontTable.get(num)); | 
 | 
        }  | 
 | 
    }  | 
 | 
}  | 
 | 
 | 
 | 
 | 
 | 
 *  color table is set to an array containing the read colors. */  | 
 | 
class ColortblDestination implements Destination  | 
 | 
{ | 
 | 
    int red, green, blue;  | 
 | 
    Vector<Color> proTemTable;  | 
 | 
 | 
 | 
    public ColortblDestination()  | 
 | 
    { | 
 | 
        red = 0;  | 
 | 
        green = 0;  | 
 | 
        blue = 0;  | 
 | 
        proTemTable = new Vector<Color>();  | 
 | 
    }  | 
 | 
 | 
 | 
    public void handleText(String text)  | 
 | 
    { | 
 | 
        int index;  | 
 | 
 | 
 | 
        for (index = 0; index < text.length(); index ++) { | 
 | 
            if (text.charAt(index) == ';') { | 
 | 
                Color newColor;  | 
 | 
                newColor = new Color(red, green, blue);  | 
 | 
                proTemTable.addElement(newColor);  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public void close()  | 
 | 
    { | 
 | 
        int count = proTemTable.size();  | 
 | 
        warning("Done reading color table, " + count + " entries."); | 
 | 
        colorTable = new Color[count];  | 
 | 
        proTemTable.copyInto(colorTable);  | 
 | 
    }  | 
 | 
 | 
 | 
    public boolean handleKeyword(String keyword, int parameter)  | 
 | 
    { | 
 | 
        if (keyword.equals("red")) | 
 | 
            red = parameter;  | 
 | 
        else if (keyword.equals("green")) | 
 | 
            green = parameter;  | 
 | 
        else if (keyword.equals("blue")) | 
 | 
            blue = parameter;  | 
 | 
        else  | 
 | 
            return false;  | 
 | 
 | 
 | 
        return true;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
    public boolean handleKeyword(String keyword) { return false; } | 
 | 
 | 
 | 
      | 
 | 
    public void begingroup() {} | 
 | 
    public void endgroup(Dictionary oldState) {} | 
 | 
 | 
 | 
      | 
 | 
    public void handleBinaryBlob(byte[] data) {} | 
 | 
}  | 
 | 
 | 
 | 
 | 
 | 
 *  into the three style arrays in the RTFReader. */  | 
 | 
class StylesheetDestination  | 
 | 
    extends DiscardingDestination  | 
 | 
    implements Destination  | 
 | 
{ | 
 | 
    Dictionary<Integer, StyleDefiningDestination> definedStyles;  | 
 | 
 | 
 | 
    public StylesheetDestination()  | 
 | 
    { | 
 | 
        definedStyles = new Hashtable<Integer, StyleDefiningDestination>();  | 
 | 
    }  | 
 | 
 | 
 | 
    public void begingroup()  | 
 | 
    { | 
 | 
        setRTFDestination(new StyleDefiningDestination());  | 
 | 
    }  | 
 | 
 | 
 | 
    public void close()  | 
 | 
    { | 
 | 
        Vector<Style> chrStyles = new Vector<Style>();  | 
 | 
        Vector<Style> pgfStyles = new Vector<Style>();  | 
 | 
        Vector<Style> secStyles = new Vector<Style>();  | 
 | 
        Enumeration<StyleDefiningDestination> styles = definedStyles.elements();  | 
 | 
        while(styles.hasMoreElements()) { | 
 | 
            StyleDefiningDestination style;  | 
 | 
            Style defined;  | 
 | 
            style = styles.nextElement();  | 
 | 
            defined = style.realize();  | 
 | 
            warning("Style "+style.number+" ("+style.styleName+"): "+defined); | 
 | 
            String stype = (String)defined.getAttribute(Constants.StyleType);  | 
 | 
            Vector<Style> toSet;  | 
 | 
            if (stype.equals(Constants.STSection)) { | 
 | 
                toSet = secStyles;  | 
 | 
            } else if (stype.equals(Constants.STCharacter)) { | 
 | 
                toSet = chrStyles;  | 
 | 
            } else { | 
 | 
                toSet = pgfStyles;  | 
 | 
            }  | 
 | 
            if (toSet.size() <= style.number)  | 
 | 
                toSet.setSize(style.number + 1);  | 
 | 
            toSet.setElementAt(defined, style.number);  | 
 | 
        }  | 
 | 
        if (!(chrStyles.isEmpty())) { | 
 | 
            Style[] styleArray = new Style[chrStyles.size()];  | 
 | 
            chrStyles.copyInto(styleArray);  | 
 | 
            characterStyles = styleArray;  | 
 | 
        }  | 
 | 
        if (!(pgfStyles.isEmpty())) { | 
 | 
            Style[] styleArray = new Style[pgfStyles.size()];  | 
 | 
            pgfStyles.copyInto(styleArray);  | 
 | 
            paragraphStyles = styleArray;  | 
 | 
        }  | 
 | 
        if (!(secStyles.isEmpty())) { | 
 | 
            Style[] styleArray = new Style[secStyles.size()];  | 
 | 
            secStyles.copyInto(styleArray);  | 
 | 
            sectionStyles = styleArray;  | 
 | 
        }  | 
 | 
 | 
 | 
/* (old debugging code)  | 
 | 
        int i, m;  | 
 | 
        if (characterStyles != null) { | 
 | 
          m = characterStyles.length;  | 
 | 
          for(i=0;i<m;i++)  | 
 | 
            warnings.println("chrStyle["+i+"]="+characterStyles[i]); | 
 | 
        } else warnings.println("No character styles."); | 
 | 
        if (paragraphStyles != null) { | 
 | 
          m = paragraphStyles.length;  | 
 | 
          for(i=0;i<m;i++)  | 
 | 
            warnings.println("pgfStyle["+i+"]="+paragraphStyles[i]); | 
 | 
        } else warnings.println("No paragraph styles."); | 
 | 
        if (sectionStyles != null) { | 
 | 
          m = characterStyles.length;  | 
 | 
          for(i=0;i<m;i++)  | 
 | 
            warnings.println("secStyle["+i+"]="+sectionStyles[i]); | 
 | 
        } else warnings.println("No section styles."); | 
 | 
*/  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
    class StyleDefiningDestination  | 
 | 
        extends AttributeTrackingDestination  | 
 | 
        implements Destination  | 
 | 
    { | 
 | 
        final int STYLENUMBER_NONE = 222;  | 
 | 
        boolean additive;  | 
 | 
        boolean characterStyle;  | 
 | 
        boolean sectionStyle;  | 
 | 
        public String styleName;  | 
 | 
        public int number;  | 
 | 
        int basedOn;  | 
 | 
        int nextStyle;  | 
 | 
        boolean hidden;  | 
 | 
 | 
 | 
        Style realizedStyle;  | 
 | 
 | 
 | 
        public StyleDefiningDestination()  | 
 | 
        { | 
 | 
            additive = false;  | 
 | 
            characterStyle = false;  | 
 | 
            sectionStyle = false;  | 
 | 
            styleName = null;  | 
 | 
            number = 0;  | 
 | 
            basedOn = STYLENUMBER_NONE;  | 
 | 
            nextStyle = STYLENUMBER_NONE;  | 
 | 
            hidden = false;  | 
 | 
        }  | 
 | 
 | 
 | 
        public void handleText(String text)  | 
 | 
        { | 
 | 
            if (styleName != null)  | 
 | 
                styleName = styleName + text;  | 
 | 
            else  | 
 | 
                styleName = text;  | 
 | 
        }  | 
 | 
 | 
 | 
        public void close() { | 
 | 
            int semicolon = (styleName == null) ? 0 : styleName.indexOf(';'); | 
 | 
            if (semicolon > 0)  | 
 | 
                styleName = styleName.substring(0, semicolon);  | 
 | 
            definedStyles.put(Integer.valueOf(number), this);  | 
 | 
            super.close();  | 
 | 
        }  | 
 | 
 | 
 | 
        public boolean handleKeyword(String keyword)  | 
 | 
        { | 
 | 
            if (keyword.equals("additive")) { | 
 | 
                additive = true;  | 
 | 
                return true;  | 
 | 
            }  | 
 | 
            if (keyword.equals("shidden")) { | 
 | 
                hidden = true;  | 
 | 
                return true;  | 
 | 
            }  | 
 | 
            return super.handleKeyword(keyword);  | 
 | 
        }  | 
 | 
 | 
 | 
        public boolean handleKeyword(String keyword, int parameter)  | 
 | 
        { | 
 | 
            if (keyword.equals("s")) { | 
 | 
                characterStyle = false;  | 
 | 
                sectionStyle = false;  | 
 | 
                number = parameter;  | 
 | 
            } else if (keyword.equals("cs")) { | 
 | 
                characterStyle = true;  | 
 | 
                sectionStyle = false;  | 
 | 
                number = parameter;  | 
 | 
            } else if (keyword.equals("ds")) { | 
 | 
                characterStyle = false;  | 
 | 
                sectionStyle = true;  | 
 | 
                number = parameter;  | 
 | 
            } else if (keyword.equals("sbasedon")) { | 
 | 
                basedOn = parameter;  | 
 | 
            } else if (keyword.equals("snext")) { | 
 | 
                nextStyle = parameter;  | 
 | 
            } else { | 
 | 
                return super.handleKeyword(keyword, parameter);  | 
 | 
            }  | 
 | 
            return true;  | 
 | 
        }  | 
 | 
 | 
 | 
        public Style realize()  | 
 | 
        { | 
 | 
            Style basis = null;  | 
 | 
            Style next = null;  | 
 | 
 | 
 | 
            if (realizedStyle != null)  | 
 | 
                return realizedStyle;  | 
 | 
 | 
 | 
            if (basedOn != STYLENUMBER_NONE) { | 
 | 
                StyleDefiningDestination styleDest;  | 
 | 
                styleDest = definedStyles.get(Integer.valueOf(basedOn));  | 
 | 
                if (styleDest != null && styleDest != this) { | 
 | 
                    basis = styleDest.realize();  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
 | 
 | 
               user. */  | 
 | 
            realizedStyle = target.addStyle(styleName, basis);  | 
 | 
 | 
 | 
            if (characterStyle) { | 
 | 
                realizedStyle.addAttributes(currentTextAttributes());  | 
 | 
                realizedStyle.addAttribute(Constants.StyleType,  | 
 | 
                                           Constants.STCharacter);  | 
 | 
            } else if (sectionStyle) { | 
 | 
                realizedStyle.addAttributes(currentSectionAttributes());  | 
 | 
                realizedStyle.addAttribute(Constants.StyleType,  | 
 | 
                                           Constants.STSection);  | 
 | 
            } else {  | 
 | 
                realizedStyle.addAttributes(currentParagraphAttributes());  | 
 | 
                realizedStyle.addAttribute(Constants.StyleType,  | 
 | 
                                           Constants.STParagraph);  | 
 | 
            }  | 
 | 
 | 
 | 
            if (nextStyle != STYLENUMBER_NONE) { | 
 | 
                StyleDefiningDestination styleDest;  | 
 | 
                styleDest = definedStyles.get(Integer.valueOf(nextStyle));  | 
 | 
                if (styleDest != null) { | 
 | 
                    next = styleDest.realize();  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            if (next != null)  | 
 | 
                realizedStyle.addAttribute(Constants.StyleNext, next);  | 
 | 
            realizedStyle.addAttribute(Constants.StyleAdditive,  | 
 | 
                                       Boolean.valueOf(additive));  | 
 | 
            realizedStyle.addAttribute(Constants.StyleHidden,  | 
 | 
                                       Boolean.valueOf(hidden));  | 
 | 
 | 
 | 
            return realizedStyle;  | 
 | 
        }  | 
 | 
    }  | 
 | 
}  | 
 | 
 | 
 | 
 | 
 | 
 *  so this is a subclass of DiscardingDestination. */  | 
 | 
class InfoDestination  | 
 | 
    extends DiscardingDestination  | 
 | 
    implements Destination  | 
 | 
{ | 
 | 
}  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 *  to the subclasses to determine what is done with the actual text. */  | 
 | 
abstract class AttributeTrackingDestination implements Destination  | 
 | 
{ | 
 | 
      | 
 | 
     *  more efficient use */  | 
 | 
    MutableAttributeSet characterAttributes;  | 
 | 
      | 
 | 
     *  more efficient use */  | 
 | 
    MutableAttributeSet paragraphAttributes;  | 
 | 
      | 
 | 
     *  more efficient use */  | 
 | 
    MutableAttributeSet sectionAttributes;  | 
 | 
 | 
 | 
    public AttributeTrackingDestination()  | 
 | 
    { | 
 | 
        characterAttributes = rootCharacterAttributes();  | 
 | 
        parserState.put("chr", characterAttributes); | 
 | 
        paragraphAttributes = rootParagraphAttributes();  | 
 | 
        parserState.put("pgf", paragraphAttributes); | 
 | 
        sectionAttributes = rootSectionAttributes();  | 
 | 
        parserState.put("sec", sectionAttributes); | 
 | 
    }  | 
 | 
 | 
 | 
    abstract public void handleText(String text);  | 
 | 
 | 
 | 
    public void handleBinaryBlob(byte[] data)  | 
 | 
    { | 
 | 
          | 
 | 
 | 
 | 
         * is more convenient. */  | 
 | 
        warning("Unexpected binary data in RTF file."); | 
 | 
    }  | 
 | 
 | 
 | 
    public void begingroup()  | 
 | 
    { | 
 | 
        AttributeSet characterParent = currentTextAttributes();  | 
 | 
        AttributeSet paragraphParent = currentParagraphAttributes();  | 
 | 
        AttributeSet sectionParent = currentSectionAttributes();  | 
 | 
 | 
 | 
        /* It would probably be more efficient to use the  | 
 | 
         * resolver property of the attributes set for  | 
 | 
         * implementing rtf groups,  | 
 | 
         * but that's needed for styles. */  | 
 | 
 | 
 | 
          | 
 | 
        characterAttributes = new SimpleAttributeSet();  | 
 | 
        characterAttributes.addAttributes(characterParent);  | 
 | 
        parserState.put("chr", characterAttributes); | 
 | 
 | 
 | 
        paragraphAttributes = new SimpleAttributeSet();  | 
 | 
        paragraphAttributes.addAttributes(paragraphParent);  | 
 | 
        parserState.put("pgf", paragraphAttributes); | 
 | 
 | 
 | 
        sectionAttributes = new SimpleAttributeSet();  | 
 | 
        sectionAttributes.addAttributes(sectionParent);  | 
 | 
        parserState.put("sec", sectionAttributes); | 
 | 
    }  | 
 | 
 | 
 | 
    public void endgroup(Dictionary oldState)  | 
 | 
    { | 
 | 
        characterAttributes = (MutableAttributeSet)parserState.get("chr"); | 
 | 
        paragraphAttributes = (MutableAttributeSet)parserState.get("pgf"); | 
 | 
        sectionAttributes   = (MutableAttributeSet)parserState.get("sec"); | 
 | 
    }  | 
 | 
 | 
 | 
    public void close()  | 
 | 
    { | 
 | 
    }  | 
 | 
 | 
 | 
    public boolean handleKeyword(String keyword)  | 
 | 
    { | 
 | 
        if (keyword.equals("ulnone")) { | 
 | 
            return handleKeyword("ul", 0); | 
 | 
        }  | 
 | 
 | 
 | 
        { | 
 | 
            RTFAttribute attr = straightforwardAttributes.get(keyword);  | 
 | 
            if (attr != null) { | 
 | 
                boolean ok;  | 
 | 
 | 
 | 
                switch(attr.domain()) { | 
 | 
                  case RTFAttribute.D_CHARACTER:  | 
 | 
                    ok = attr.set(characterAttributes);  | 
 | 
                    break;  | 
 | 
                  case RTFAttribute.D_PARAGRAPH:  | 
 | 
                    ok = attr.set(paragraphAttributes);  | 
 | 
                    break;  | 
 | 
                  case RTFAttribute.D_SECTION:  | 
 | 
                    ok = attr.set(sectionAttributes);  | 
 | 
                    break;  | 
 | 
                  case RTFAttribute.D_META:  | 
 | 
                    mockery.backing = parserState;  | 
 | 
                    ok = attr.set(mockery);  | 
 | 
                    mockery.backing = null;  | 
 | 
                    break;  | 
 | 
                  case RTFAttribute.D_DOCUMENT:  | 
 | 
                    ok = attr.set(documentAttributes);  | 
 | 
                    break;  | 
 | 
                  default:  | 
 | 
                      | 
 | 
                    ok = false;  | 
 | 
                    break;  | 
 | 
                }  | 
 | 
                if (ok)  | 
 | 
                    return true;  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
 | 
 | 
        if (keyword.equals("plain")) { | 
 | 
            resetCharacterAttributes();  | 
 | 
            return true;  | 
 | 
        }  | 
 | 
 | 
 | 
        if (keyword.equals("pard")) { | 
 | 
            resetParagraphAttributes();  | 
 | 
            return true;  | 
 | 
        }  | 
 | 
 | 
 | 
        if (keyword.equals("sectd")) { | 
 | 
            resetSectionAttributes();  | 
 | 
            return true;  | 
 | 
        }  | 
 | 
 | 
 | 
        return false;  | 
 | 
    }  | 
 | 
 | 
 | 
    public boolean handleKeyword(String keyword, int parameter)  | 
 | 
    { | 
 | 
        boolean booleanParameter = (parameter != 0);  | 
 | 
 | 
 | 
        if (keyword.equals("fc")) | 
 | 
            keyword = "cf"; /* whatEVER, dude. */  | 
 | 
 | 
 | 
        if (keyword.equals("f")) { | 
 | 
            parserState.put(keyword, Integer.valueOf(parameter));  | 
 | 
            return true;  | 
 | 
        }  | 
 | 
        if (keyword.equals("cf")) { | 
 | 
            parserState.put(keyword, Integer.valueOf(parameter));  | 
 | 
            return true;  | 
 | 
        }  | 
 | 
 | 
 | 
        { | 
 | 
            RTFAttribute attr = straightforwardAttributes.get(keyword);  | 
 | 
            if (attr != null) { | 
 | 
                boolean ok;  | 
 | 
 | 
 | 
                switch(attr.domain()) { | 
 | 
                  case RTFAttribute.D_CHARACTER:  | 
 | 
                    ok = attr.set(characterAttributes, parameter);  | 
 | 
                    break;  | 
 | 
                  case RTFAttribute.D_PARAGRAPH:  | 
 | 
                    ok = attr.set(paragraphAttributes, parameter);  | 
 | 
                    break;  | 
 | 
                  case RTFAttribute.D_SECTION:  | 
 | 
                    ok = attr.set(sectionAttributes, parameter);  | 
 | 
                    break;  | 
 | 
                  case RTFAttribute.D_META:  | 
 | 
                    mockery.backing = parserState;  | 
 | 
                    ok = attr.set(mockery, parameter);  | 
 | 
                    mockery.backing = null;  | 
 | 
                    break;  | 
 | 
                  case RTFAttribute.D_DOCUMENT:  | 
 | 
                    ok = attr.set(documentAttributes, parameter);  | 
 | 
                    break;  | 
 | 
                  default:  | 
 | 
                      | 
 | 
                    ok = false;  | 
 | 
                    break;  | 
 | 
                }  | 
 | 
                if (ok)  | 
 | 
                    return true;  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        if (keyword.equals("fs")) { | 
 | 
            StyleConstants.setFontSize(characterAttributes, (parameter / 2));  | 
 | 
            return true;  | 
 | 
        }  | 
 | 
 | 
 | 
        /* TODO: superscript/subscript */  | 
 | 
 | 
 | 
        if (keyword.equals("sl")) { | 
 | 
            if (parameter == 1000) {   | 
 | 
                characterAttributes.removeAttribute(StyleConstants.LineSpacing);  | 
 | 
            } else { | 
 | 
                  | 
 | 
 | 
 | 
 | 
 | 
                   handles this, also recognize the slmult keyword. */  | 
 | 
                StyleConstants.setLineSpacing(characterAttributes,  | 
 | 
                                              parameter / 20f);  | 
 | 
            }  | 
 | 
            return true;  | 
 | 
        }  | 
 | 
 | 
 | 
        /* TODO: Other kinds of underlining */  | 
 | 
 | 
 | 
        if (keyword.equals("tx") || keyword.equals("tb")) { | 
 | 
            float tabPosition = parameter / 20f;  | 
 | 
            int tabAlignment, tabLeader;  | 
 | 
            Number item;  | 
 | 
 | 
 | 
            tabAlignment = TabStop.ALIGN_LEFT;  | 
 | 
            item = (Number)(parserState.get("tab_alignment")); | 
 | 
            if (item != null)  | 
 | 
                tabAlignment = item.intValue();  | 
 | 
            tabLeader = TabStop.LEAD_NONE;  | 
 | 
            item = (Number)(parserState.get("tab_leader")); | 
 | 
            if (item != null)  | 
 | 
                tabLeader = item.intValue();  | 
 | 
            if (keyword.equals("tb")) | 
 | 
                tabAlignment = TabStop.ALIGN_BAR;  | 
 | 
 | 
 | 
            parserState.remove("tab_alignment"); | 
 | 
            parserState.remove("tab_leader"); | 
 | 
 | 
 | 
            TabStop newStop = new TabStop(tabPosition, tabAlignment, tabLeader);  | 
 | 
            Dictionary<Object, Object> tabs;  | 
 | 
            Integer stopCount;  | 
 | 
 | 
 | 
            tabs = (Dictionary<Object, Object>)parserState.get("_tabs"); | 
 | 
            if (tabs == null) { | 
 | 
                tabs = new Hashtable<Object, Object>();  | 
 | 
                parserState.put("_tabs", tabs); | 
 | 
                stopCount = Integer.valueOf(1);  | 
 | 
            } else { | 
 | 
                stopCount = (Integer)tabs.get("stop count"); | 
 | 
                stopCount = Integer.valueOf(1 + stopCount.intValue());  | 
 | 
            }  | 
 | 
            tabs.put(stopCount, newStop);  | 
 | 
            tabs.put("stop count", stopCount); | 
 | 
            parserState.remove("_tabs_immutable"); | 
 | 
 | 
 | 
            return true;  | 
 | 
        }  | 
 | 
 | 
 | 
        if (keyword.equals("s") && | 
 | 
            paragraphStyles != null) { | 
 | 
            parserState.put("paragraphStyle", paragraphStyles[parameter]); | 
 | 
            return true;  | 
 | 
        }  | 
 | 
 | 
 | 
        if (keyword.equals("cs") && | 
 | 
            characterStyles != null) { | 
 | 
            parserState.put("characterStyle", characterStyles[parameter]); | 
 | 
            return true;  | 
 | 
        }  | 
 | 
 | 
 | 
        if (keyword.equals("ds") && | 
 | 
            sectionStyles != null) { | 
 | 
            parserState.put("sectionStyle", sectionStyles[parameter]); | 
 | 
            return true;  | 
 | 
        }  | 
 | 
 | 
 | 
        return false;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
     *  default character attributes */  | 
 | 
    protected MutableAttributeSet rootCharacterAttributes()  | 
 | 
    { | 
 | 
        MutableAttributeSet set = new SimpleAttributeSet();  | 
 | 
 | 
 | 
        /* TODO: default font */  | 
 | 
 | 
 | 
        StyleConstants.setItalic(set, false);  | 
 | 
        StyleConstants.setBold(set, false);  | 
 | 
        StyleConstants.setUnderline(set, false);  | 
 | 
        StyleConstants.setForeground(set, defaultColor());  | 
 | 
 | 
 | 
        return set;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
     *  default paragraph attributes */  | 
 | 
    protected MutableAttributeSet rootParagraphAttributes()  | 
 | 
    { | 
 | 
        MutableAttributeSet set = new SimpleAttributeSet();  | 
 | 
 | 
 | 
        StyleConstants.setLeftIndent(set, 0f);  | 
 | 
        StyleConstants.setRightIndent(set, 0f);  | 
 | 
        StyleConstants.setFirstLineIndent(set, 0f);  | 
 | 
 | 
 | 
          | 
 | 
        set.setResolveParent(target.getStyle(StyleContext.DEFAULT_STYLE));  | 
 | 
 | 
 | 
        return set;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
     *  default section attributes */  | 
 | 
    protected MutableAttributeSet rootSectionAttributes()  | 
 | 
    { | 
 | 
        MutableAttributeSet set = new SimpleAttributeSet();  | 
 | 
 | 
 | 
        return set;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    MutableAttributeSet currentTextAttributes()  | 
 | 
    { | 
 | 
        MutableAttributeSet attributes =  | 
 | 
            new SimpleAttributeSet(characterAttributes);  | 
 | 
        Integer fontnum;  | 
 | 
        Integer stateItem;  | 
 | 
 | 
 | 
        /* figure out the font name */  | 
 | 
          | 
 | 
 | 
 | 
           job to clean up after corrupt RTF) */  | 
 | 
        fontnum = (Integer)parserState.get("f"); | 
 | 
          | 
 | 
        String fontFamily;  | 
 | 
        if (fontnum != null)  | 
 | 
            fontFamily = fontTable.get(fontnum);  | 
 | 
        else  | 
 | 
            fontFamily = null;  | 
 | 
        if (fontFamily != null)  | 
 | 
            StyleConstants.setFontFamily(attributes, fontFamily);  | 
 | 
        else  | 
 | 
            attributes.removeAttribute(StyleConstants.FontFamily);  | 
 | 
 | 
 | 
        if (colorTable != null) { | 
 | 
            stateItem = (Integer)parserState.get("cf"); | 
 | 
            if (stateItem != null) { | 
 | 
                Color fg = colorTable[stateItem.intValue()];  | 
 | 
                StyleConstants.setForeground(attributes, fg);  | 
 | 
            } else { | 
 | 
                  | 
 | 
                attributes.removeAttribute(StyleConstants.Foreground);  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        if (colorTable != null) { | 
 | 
            stateItem = (Integer)parserState.get("cb"); | 
 | 
            if (stateItem != null) { | 
 | 
                Color bg = colorTable[stateItem.intValue()];  | 
 | 
                attributes.addAttribute(StyleConstants.Background,  | 
 | 
                                        bg);  | 
 | 
            } else { | 
 | 
                  | 
 | 
                attributes.removeAttribute(StyleConstants.Background);  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        Style characterStyle = (Style)parserState.get("characterStyle"); | 
 | 
        if (characterStyle != null)  | 
 | 
            attributes.setResolveParent(characterStyle);  | 
 | 
 | 
 | 
        /* Other attributes are maintained directly in "attributes" */  | 
 | 
 | 
 | 
        return attributes;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    MutableAttributeSet currentParagraphAttributes()  | 
 | 
    { | 
 | 
          | 
 | 
        MutableAttributeSet bld = new SimpleAttributeSet(paragraphAttributes);  | 
 | 
 | 
 | 
        Integer stateItem;  | 
 | 
 | 
 | 
          | 
 | 
        TabStop tabs[];  | 
 | 
 | 
 | 
        tabs = (TabStop[])parserState.get("_tabs_immutable"); | 
 | 
        if (tabs == null) { | 
 | 
            Dictionary workingTabs = (Dictionary)parserState.get("_tabs"); | 
 | 
            if (workingTabs != null) { | 
 | 
                int count = ((Integer)workingTabs.get("stop count")).intValue(); | 
 | 
                tabs = new TabStop[count];  | 
 | 
                for (int ix = 1; ix <= count; ix ++)  | 
 | 
                    tabs[ix-1] = (TabStop)workingTabs.get(Integer.valueOf(ix));  | 
 | 
                parserState.put("_tabs_immutable", tabs); | 
 | 
            }  | 
 | 
        }  | 
 | 
        if (tabs != null)  | 
 | 
            bld.addAttribute(Constants.Tabs, tabs);  | 
 | 
 | 
 | 
        Style paragraphStyle = (Style)parserState.get("paragraphStyle"); | 
 | 
        if (paragraphStyle != null)  | 
 | 
            bld.setResolveParent(paragraphStyle);  | 
 | 
 | 
 | 
        return bld;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public AttributeSet currentSectionAttributes()  | 
 | 
    { | 
 | 
        MutableAttributeSet attributes = new SimpleAttributeSet(sectionAttributes);  | 
 | 
 | 
 | 
        Style sectionStyle = (Style)parserState.get("sectionStyle"); | 
 | 
        if (sectionStyle != null)  | 
 | 
            attributes.setResolveParent(sectionStyle);  | 
 | 
 | 
 | 
        return attributes;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     *  \plain keyword. */  | 
 | 
    protected void resetCharacterAttributes()  | 
 | 
    { | 
 | 
        handleKeyword("f", 0); | 
 | 
        handleKeyword("cf", 0); | 
 | 
 | 
 | 
        handleKeyword("fs", 24);  /* 12 pt. */ | 
 | 
 | 
 | 
        Enumeration<RTFAttribute> attributes = straightforwardAttributes.elements();  | 
 | 
        while(attributes.hasMoreElements()) { | 
 | 
            RTFAttribute attr = attributes.nextElement();  | 
 | 
            if (attr.domain() == RTFAttribute.D_CHARACTER)  | 
 | 
                attr.setDefault(characterAttributes);  | 
 | 
        }  | 
 | 
 | 
 | 
        handleKeyword("sl", 1000); | 
 | 
 | 
 | 
        parserState.remove("characterStyle"); | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     *  \pard keyword. */  | 
 | 
    protected void resetParagraphAttributes()  | 
 | 
    { | 
 | 
        parserState.remove("_tabs"); | 
 | 
        parserState.remove("_tabs_immutable"); | 
 | 
        parserState.remove("paragraphStyle"); | 
 | 
 | 
 | 
        StyleConstants.setAlignment(paragraphAttributes,  | 
 | 
                                    StyleConstants.ALIGN_LEFT);  | 
 | 
 | 
 | 
        Enumeration<RTFAttribute> attributes = straightforwardAttributes.elements();  | 
 | 
        while(attributes.hasMoreElements()) { | 
 | 
            RTFAttribute attr = attributes.nextElement();  | 
 | 
            if (attr.domain() == RTFAttribute.D_PARAGRAPH)  | 
 | 
                attr.setDefault(characterAttributes);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     *  \sectd keyword. */  | 
 | 
    protected void resetSectionAttributes()  | 
 | 
    { | 
 | 
        Enumeration<RTFAttribute> attributes = straightforwardAttributes.elements();  | 
 | 
        while(attributes.hasMoreElements()) { | 
 | 
            RTFAttribute attr = attributes.nextElement();  | 
 | 
            if (attr.domain() == RTFAttribute.D_SECTION)  | 
 | 
                attr.setDefault(characterAttributes);  | 
 | 
        }  | 
 | 
 | 
 | 
        parserState.remove("sectionStyle"); | 
 | 
    }  | 
 | 
}  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
abstract class TextHandlingDestination  | 
 | 
    extends AttributeTrackingDestination  | 
 | 
    implements Destination  | 
 | 
{ | 
 | 
      | 
 | 
     *  a paragraph; false upon startup */  | 
 | 
    boolean inParagraph;  | 
 | 
 | 
 | 
    public TextHandlingDestination()  | 
 | 
    { | 
 | 
        super();  | 
 | 
        inParagraph = false;  | 
 | 
    }  | 
 | 
 | 
 | 
    public void handleText(String text)  | 
 | 
    { | 
 | 
        if (! inParagraph)  | 
 | 
            beginParagraph();  | 
 | 
 | 
 | 
        deliverText(text, currentTextAttributes());  | 
 | 
    }  | 
 | 
 | 
 | 
    abstract void deliverText(String text, AttributeSet characterAttributes);  | 
 | 
 | 
 | 
    public void close()  | 
 | 
    { | 
 | 
        if (inParagraph)  | 
 | 
            endParagraph();  | 
 | 
 | 
 | 
        super.close();  | 
 | 
    }  | 
 | 
 | 
 | 
    public boolean handleKeyword(String keyword)  | 
 | 
    { | 
 | 
        if (keyword.equals("\r") || keyword.equals("\n")) { | 
 | 
            keyword = "par";  | 
 | 
        }  | 
 | 
 | 
 | 
        if (keyword.equals("par")) { | 
 | 
 | 
 | 
            endParagraph();  | 
 | 
            return true;  | 
 | 
        }  | 
 | 
 | 
 | 
        if (keyword.equals("sect")) { | 
 | 
 | 
 | 
            endSection();  | 
 | 
            return true;  | 
 | 
        }  | 
 | 
 | 
 | 
        return super.handleKeyword(keyword);  | 
 | 
    }  | 
 | 
 | 
 | 
    protected void beginParagraph()  | 
 | 
    { | 
 | 
        inParagraph = true;  | 
 | 
    }  | 
 | 
 | 
 | 
    protected void endParagraph()  | 
 | 
    { | 
 | 
        AttributeSet pgfAttributes = currentParagraphAttributes();  | 
 | 
        AttributeSet chrAttributes = currentTextAttributes();  | 
 | 
        finishParagraph(pgfAttributes, chrAttributes);  | 
 | 
        inParagraph = false;  | 
 | 
    }  | 
 | 
 | 
 | 
    abstract void finishParagraph(AttributeSet pgfA, AttributeSet chrA);  | 
 | 
 | 
 | 
    abstract void endSection();  | 
 | 
}  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
class DocumentDestination  | 
 | 
    extends TextHandlingDestination  | 
 | 
    implements Destination  | 
 | 
{ | 
 | 
    public void deliverText(String text, AttributeSet characterAttributes)  | 
 | 
    { | 
 | 
        try { | 
 | 
            target.insertString(target.getLength(),  | 
 | 
                                text,  | 
 | 
                                currentTextAttributes());  | 
 | 
        } catch (BadLocationException ble) { | 
 | 
            /* This shouldn't be able to happen, of course */  | 
 | 
              | 
 | 
            throw new InternalError(ble.getMessage(), ble);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public void finishParagraph(AttributeSet pgfAttributes,  | 
 | 
                                AttributeSet chrAttributes)  | 
 | 
    { | 
 | 
        int pgfEndPosition = target.getLength();  | 
 | 
        try { | 
 | 
            target.insertString(pgfEndPosition, "\n", chrAttributes);  | 
 | 
            target.setParagraphAttributes(pgfEndPosition, 1, pgfAttributes, true);  | 
 | 
        } catch (BadLocationException ble) { | 
 | 
            /* This shouldn't be able to happen, of course */  | 
 | 
              | 
 | 
            throw new InternalError(ble.getMessage(), ble);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public void endSection()  | 
 | 
    { | 
 | 
        /* If we implemented sections, we'd end 'em here */  | 
 | 
    }  | 
 | 
}  | 
 | 
 | 
 | 
}  |